diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..a2935439b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Create a report to help us improve. + +If the bug is a security issue, please **do not fill an issue**, and instead send a mail to . + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**geOrchestra version or branch** + +eg: 17.12 or 18.06 or master ? + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..6a925bbab1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,32 @@ +version: 2 +updates: +- package-ecosystem: maven + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 10 + target-branch: master + assignees: + - RemiDesgrange + ignore: + - dependency-name: org.webjars:extjs + versions: + - "> 4.1.1a" +- package-ecosystem: gitsubmodule + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + target-branch: master +- package-ecosystem: gitsubmodule + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + target-branch: 20.1.x +- package-ecosystem: gitsubmodule + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + target-branch: 22.0.x diff --git a/.github/resources/m2-settings.xml b/.github/resources/m2-settings.xml new file mode 100644 index 0000000000..9672c8dc2c --- /dev/null +++ b/.github/resources/m2-settings.xml @@ -0,0 +1,10 @@ + + + + + artifactory-georchestra + * + https://artifactory.georchestra.org/artifactory/maven + + + diff --git a/.github/workflows/analytics.yml b/.github/workflows/analytics.yml new file mode 100644 index 0000000000..5f20d74da0 --- /dev/null +++ b/.github/workflows/analytics.yml @@ -0,0 +1,81 @@ +name: "analytics" +on: + workflow_dispatch: + push: + paths: + - "commons/**" + - "analytics/**" + pull_request: + paths: + - "commons/**" + - "analytics/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: "Setting up Java" + uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: "Configuring Maven" + run: | + mkdir -p $HOME/.m2 + cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Maven repository caching" + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: "Installing & checking formatting" + run: ./mvnw --no-transfer-progress -B -P-all,analytics -Dfmt.action=validate install -Dadditionalparam=-Xdoclint:none -DskipTests + + - name: "Running tests" + working-directory: analytics/ + run: ../mvnw --no-transfer-progress clean verify -Pit -Dfmt.skip=true -Dadditionalparam=-Xdoclint:none + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image" + if: github.repository == 'georchestra/georchestra' + working-directory: analytics/ + run: ../mvnw --no-transfer-progress clean package docker:build -Pdocker,log4j-logstash,sentry-log4j -DdockerImageName=georchestra/analytics:${{ steps.version.outputs.VERSION }} -DskipTests + + - name: "Logging in docker.io" + if: github.repository == 'georchestra/georchestra' + uses: azure/docker-login@v1 + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/analytics:${{ steps.version.outputs.VERSION }} georchestra/analytics:latest + docker push georchestra/analytics:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/analytics:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/analytics:${{ steps.version.outputs.VERSION }} + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.github/workflows/atlas.yml b/.github/workflows/atlas.yml new file mode 100644 index 0000000000..1d5101c45c --- /dev/null +++ b/.github/workflows/atlas.yml @@ -0,0 +1,81 @@ +name: "atlas" +on: + workflow_dispatch: + push: + paths: + - "commons/**" + - "atlas/**" + pull_request: + paths: + - "commons/**" + - "atlas/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: "Setting up Java" + uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: "Configuring Maven" + run: | + mkdir -p $HOME/.m2 + cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Maven repository caching" + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: "Installing & checking formatting" + run: ./mvnw --no-transfer-progress -B -P-all,atlas -Dfmt.action=validate install -Dadditionalparam=-Xdoclint:none -DskipTests + + - name: "Running tests" + working-directory: atlas/ + run: ../mvnw --no-transfer-progress clean verify -Pit -Dfmt.skip=true -Dadditionalparam=-Xdoclint:none + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image" + if: github.repository == 'georchestra/georchestra' + working-directory: atlas/ + run: ../mvnw --no-transfer-progress clean package docker:build -Pdocker,log4j-logstash,sentry-log4j -DdockerImageName=georchestra/atlas:${{ steps.version.outputs.VERSION }} -DskipTests + + - name: "Logging in docker.io" + uses: azure/docker-login@v1 + if: github.repository == 'georchestra/georchestra' + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/atlas:${{ steps.version.outputs.VERSION }} georchestra/atlas:latest + docker push georchestra/atlas:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/atlas:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/atlas:${{ steps.version.outputs.VERSION }} + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.github/workflows/console.yml b/.github/workflows/console.yml new file mode 100644 index 0000000000..e369ef3c06 --- /dev/null +++ b/.github/workflows/console.yml @@ -0,0 +1,94 @@ +name: "console" +on: + workflow_dispatch: + push: + paths: + - "ldap/**" + - "postgresql/**" + - "commons/**" + - "ldap-account-management/**" + - "console/**" + pull_request: + paths: + - "ldap/**" + - "postgresql/**" + - "commons/**" + - "ldap-account-management/**" + - "console/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: "Setting up Java" + uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: "Configuring Maven" + run: | + mkdir -p $HOME/.m2 + cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Maven repository caching" + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: "Installing & checking formatting" + run: ./mvnw install -pl :console --also-make -P-all,console --no-transfer-progress -B -Dfmt.action=validate -Dadditionalparam=-Xdoclint:none -DskipTests + # note "-pl :console --also-make" builds only the project and its dependencies + + - name: "Building LDAP and PostgreSQL docker images" + if: github.repository == 'georchestra/georchestra' + run: | + docker build -t georchestra/ldap:latest ./ldap + docker build -t georchestra/database:latest ./postgresql + + - name: "Running tests" + working-directory: console/ + run: ../mvnw verify --no-transfer-progress -Dfmt.skip=true -Dadditionalparam=-Xdoclint:none -Dskip.npm + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image" + if: github.repository == 'georchestra/georchestra' + working-directory: console/ + run: ../mvnw --no-transfer-progress clean package docker:build -Pdocker,log4j-logstash,sentry-log4j -DdockerImageName=georchestra/console:${{ steps.version.outputs.VERSION }} -DskipTests + + - name: "Logging in docker.io" + if: github.repository == 'georchestra/georchestra' + uses: azure/docker-login@v1 + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/console:${{ steps.version.outputs.VERSION }} georchestra/console:latest + docker push georchestra/console:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/console:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/console:${{ steps.version.outputs.VERSION }} + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.github/workflows/database.yml b/.github/workflows/database.yml new file mode 100644 index 0000000000..3d18837573 --- /dev/null +++ b/.github/workflows/database.yml @@ -0,0 +1,49 @@ +name: "PostGreSQL" +on: + workflow_dispatch: + push: + paths: + - "postgresql/**" + pull_request: + paths: + - "postgresql/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: Getting image tag + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Docker build database" + if: github.repository == 'georchestra/georchestra' + working-directory: postgresql/ + run: docker build -t georchestra/database:${{ steps.version.outputs.VERSION }} . + + - name: "Logging in docker.io" + if: github.repository == 'georchestra/georchestra' + uses: azure/docker-login@v1 + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/database:${{ steps.version.outputs.VERSION }} georchestra/database:latest + docker push georchestra/database:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/database:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/database:${{ steps.version.outputs.VERSION }} diff --git a/.github/workflows/datafeeder.yml b/.github/workflows/datafeeder.yml new file mode 100644 index 0000000000..e0c32f3ff5 --- /dev/null +++ b/.github/workflows/datafeeder.yml @@ -0,0 +1,107 @@ +name: "datafeeder" +on: + workflow_dispatch: + push: + paths: + - "commons/**" + - "security-proxy-spring-integration/**" + - "datafeeder/**" + pull_request: + paths: + - "commons/**" + - "security-proxy-spring-integration/**" + - "datafeeder/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: "Setting up Java" + uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: "Configuring Maven" + run: | + mkdir -p $HOME/.m2 + # revisit? can't resolve org.openapitools:openapi-generator-maven-plugin:jar:5.0.1 in artifactory-georchestra + #cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Maven repository caching" + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: "Installing & checking formatting" + run: ./mvnw install -pl :datafeeder --also-make -P-all,datafeeder --no-transfer-progress -B -Dfmt.action=validate -Dadditionalparam=-Xdoclint:none -DskipTests + # note "-pl :datafeeder --also-make" builds only the project and its dependencies + + - name: "Running Unit Tests" + run: ./mvnw test -pl :datafeeder -P-all,datafeeder -ntp -Dfmt.skip=true -Dadditionalparam=-Xdoclint:none + + # pull images, fetches in parallel docker layers and speeds up the process + - name: "Pull required docker images for integration testing" + working-directory: datafeeder/ + run: docker-compose pull -q + + - name: "Build required docker images (ldap, database)" + run: | + docker build -t georchestra/ldap:latest ./ldap + docker build -t georchestra/database:latest ./postgresql + + - name: "Build required docker image (geonetwork)" + run: | + git submodule update --init --recursive --depth 1 geonetwork/ + cd geonetwork && ../mvnw install -T1C -ntp -DskipTests + cd web && ../../mvnw package docker:build -Pdocker -DdockerImageName=georchestra/geonetwork -DdockerImageTags=latest -ntp -DskipTests + + - name: "Build required docker image (console)" + run: | + ./mvnw -pl :console -am clean install docker:build -P-all,console,docker -DdockerImageName=georchestra/console:latest -DskipTests -ntp -Dskip.npm -Dfmt.skip + + - name: "Running Integration Tests" + working-directory: datafeeder/ + run: ../mvnw verify -P-all,datafeeder -DskipITs=false -DskipTests -ntp -Dfmt.skip=true -Dadditionalparam=-Xdoclint:none + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image" + if: github.repository == 'georchestra/georchestra' + run: ./mvnw -f datafeeder/ clean package docker:build -Pdocker -DskipTests -DdockerImageName=georchestra/datafeeder:${{ steps.version.outputs.VERSION }} + + - name: "Logging in docker.io" + if: github.repository == 'georchestra/georchestra' + uses: azure/docker-login@v1 + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/datafeeder:${{ steps.version.outputs.VERSION }} georchestra/datafeeder:latest + docker push georchestra/datafeeder:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/21.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/datafeeder:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/21.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/datafeeder:${{ steps.version.outputs.VERSION }} + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.github/workflows/extractorapp.yml b/.github/workflows/extractorapp.yml new file mode 100644 index 0000000000..dce66815d4 --- /dev/null +++ b/.github/workflows/extractorapp.yml @@ -0,0 +1,81 @@ +name: "extractorapp" +on: + workflow_dispatch: + push: + paths: + - "commons/**" + - "extractorapp/**" + pull_request: + paths: + - "commons/**" + - "extractorapp/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: "Setting up Java" + uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: "Configuring Maven" + run: | + mkdir -p $HOME/.m2 + cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Maven repository caching" + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: "Installing & checking formatting" + run: ./mvnw --no-transfer-progress -B -P-all,extractorapp -Dfmt.action=validate install -Dadditionalparam=-Xdoclint:none -DskipTests + + - name: "Running tests" + working-directory: extractorapp/ + run: ../mvnw --no-transfer-progress clean verify -Pit -Dfmt.skip=true -Dadditionalparam=-Xdoclint:none + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image" + if: github.repository == 'georchestra/georchestra' + working-directory: extractorapp/ + run: ../mvnw --no-transfer-progress clean package docker:build -Pdocker,log4j-logstash,sentry-log4j -DdockerImageName=georchestra/extractorapp:${{ steps.version.outputs.VERSION }} -DskipTests + + - name: "Logging in docker.io" + uses: azure/docker-login@v1 + if: github.repository == 'georchestra/georchestra' + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/extractorapp:${{ steps.version.outputs.VERSION }} georchestra/extractorapp:latest + docker push georchestra/extractorapp:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/extractorapp:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/extractorapp:${{ steps.version.outputs.VERSION }} + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.github/workflows/geonetwork.yml b/.github/workflows/geonetwork.yml new file mode 100644 index 0000000000..3694ba01b3 --- /dev/null +++ b/.github/workflows/geonetwork.yml @@ -0,0 +1,108 @@ +name: "geonetwork" +on: + workflow_dispatch: + push: + paths: + - "commons/**" + - "security-proxy-spring-integration/**" + - "geonetwork" + - ".github/workflows/geonetwork.yml" + - "ldap-account-management/**" + - "console/**" + - "ldap/**" + pull_request: + paths: + - "commons/**" + - "security-proxy-spring-integration/**" + - "geonetwork" + - "ldap-account-management/**" + - "console/**" + - "ldap/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - name: "Checking out" + uses: actions/checkout@v2 + + - name: "Checking out submodules" + run: git submodule update --init --recursive --depth 1 geonetwork/ + + - name: "Setting up Java" + uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: "Configuring Maven" + run: | + mkdir -p $HOME/.m2 + # Could not find artifact com.sun.media:jai-codec:jar:1.1.3 in artifactory-georchestra + # cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Maven repository caching" + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: "Build Georchestra dependencies" + run: ./mvnw install -pl :security-proxy-spring-integration,testcontainers --also-make -P-all -T1C -ntp -B -Dfmt.action=validate -Dadditionalparam=-Xdoclint:none + # note "-pl :datafeeder --also-make" builds only the project and its dependencies + + - name: "Build GeoNetwork" + working-directory: geonetwork/ + run: ../mvnw install -DskipTests -T1C -ntp -B -Dadditionalparam=-Xdoclint:none + + - name: "Build required docker images (ldap, database)" + run: | + docker build -t georchestra/ldap:latest ./ldap + docker build -t georchestra/database:latest ./postgresql + + - name: "Build required docker image (console)" + run: | + ./mvnw -pl :console -am clean install docker:build -P-all,console,docker -DdockerImageName=georchestra/console:latest -DskipTests -ntp -Dskip.npm -Dfmt.skip + + - name: "Run Georchestra Integration Tests" + working-directory: geonetwork/georchestra-integration/ + run: ../../mvnw verify -ntp -Dadditionalparam=-Xdoclint:none + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Build GeoNetwork docker image" + if: github.repository == 'georchestra/georchestra' + run: | + cd geonetwork/web + ../../mvnw package docker:build -Pdocker -DdockerImageName=georchestra/geonetwork -DdockerImageTags=${{ steps.version.outputs.VERSION }},latest -DskipTests -ntp + + - name: "Logging in docker.io" + uses: azure/docker-login@v1 + if: github.repository == 'georchestra/georchestra' + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/geonetwork:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/21.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/geonetwork:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/21.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/geonetwork:${{ steps.version.outputs.VERSION }} + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.github/workflows/geoserver.yml b/.github/workflows/geoserver.yml new file mode 100644 index 0000000000..6f778ec80a --- /dev/null +++ b/.github/workflows/geoserver.yml @@ -0,0 +1,99 @@ +--- +env: + # The following variable should be set to the same value as in the Makefile at the root of the repository + GEOSERVER_EXTENSION_PROFILES: colormap,mbtiles,wps-download,app-schema,control-flow,csw,inspire,libjpeg-turbo,monitor,pyramid,wps,css,s3-geotiff,jp2k,authkey,mapstore2,mbstyle,ogcapi,web-resource,flatgeobuf,sldservice + +name: "geoserver" +on: + workflow_dispatch: + push: + paths: + - "commons/**" + - "geoserver/**" + pull_request: + paths: + - "commons/**" + - "geoserver/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: "Checking out submodules" + run: git submodule update --init --recursive geoserver/geoserver-submodule + + - name: "Setting up Java" + uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: "Configuring Maven" + run: | + mkdir -p $HOME/.m2 + cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Maven repository caching" + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: "Installing & checking formatting" + run: ./mvnw --no-transfer-progress -B -P-all,geoserver,geofence -Dfmt.skip=true install -Dadditionalparam=-Xdoclint:none -DskipTests + + - name: "Running tests" + working-directory: geoserver/ + run: ../mvnw --no-transfer-progress clean verify -Pit -Dfmt.skip=true -Dadditionalparam=-Xdoclint:none + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image with native security" + if: github.repository == 'georchestra/georchestra' + working-directory: geoserver/webapp + run: ../../mvnw --no-transfer-progress clean package docker:build -Pdocker,log4j-logstash,sentry-log4j,${{ env.GEOSERVER_EXTENSION_PROFILES }} -DdockerImageName=georchestra/geoserver:${{ steps.version.outputs.VERSION }} -DskipTests + + + - name: "Building docker image with geofence" + if: github.repository == 'georchestra/georchestra' + working-directory: geoserver/webapp + run: ../../mvnw --no-transfer-progress clean package docker:build -Pdocker,${{ env.GEOSERVER_EXTENSION_PROFILES }},geofence,log4j-logstash,sentry-log4j -DdockerImageName=georchestra/geoserver:${{ steps.version.outputs.VERSION }}-geofence -DskipTests + + - name: "Logging in docker.io" + uses: azure/docker-login@v1 + if: github.repository == 'georchestra/georchestra' + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/geoserver:${{ steps.version.outputs.VERSION }} georchestra/geoserver:latest + docker tag georchestra/geoserver:${{ steps.version.outputs.VERSION }}-geofence georchestra/geoserver:geofence + docker push georchestra/geoserver:latest + docker push georchestra/geoserver:geofence + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/geoserver:${{ steps.version.outputs.VERSION }} + docker push georchestra/geoserver:${{ steps.version.outputs.VERSION }}-geofence + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/geoserver:${{ steps.version.outputs.VERSION }} + docker push georchestra/geoserver:${{ steps.version.outputs.VERSION }}-geofence + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.github/workflows/geowebcache.yml b/.github/workflows/geowebcache.yml new file mode 100644 index 0000000000..3d4c67186b --- /dev/null +++ b/.github/workflows/geowebcache.yml @@ -0,0 +1,81 @@ +name: "geowebcache" +on: + workflow_dispatch: + push: + paths: + - "commons/**" + - "geowebcache-webapp/**" + pull_request: + paths: + - "commons/**" + - "geowebcache-webapp/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: "Setting up Java" + uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: "Configuring Maven" + run: | + mkdir -p $HOME/.m2 + cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Maven repository caching" + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: "Installing & checking formatting" + run: ./mvnw --no-transfer-progress -B -P-all,geowebcache -Dfmt.action=validate install -Dadditionalparam=-Xdoclint:none -DskipTests + + - name: "Running tests" + working-directory: geowebcache-webapp/ + run: ../mvnw --no-transfer-progress clean verify -Pit -Dfmt.skip=true -Dadditionalparam=-Xdoclint:none + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image" + if: github.repository == 'georchestra/georchestra' + working-directory: geowebcache-webapp/ + run: ../mvnw --no-transfer-progress clean package docker:build -Pdocker,log4j-logstash,sentry-log4j -DdockerImageName=georchestra/geowebcache:${{ steps.version.outputs.VERSION }} -DskipTests + + - name: "Logging in docker.io" + if: github.repository == 'georchestra/georchestra' + uses: azure/docker-login@v1 + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/geowebcache:${{ steps.version.outputs.VERSION }} georchestra/geowebcache:latest + docker push georchestra/geowebcache:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/geowebcache:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/geowebcache:${{ steps.version.outputs.VERSION }} + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.github/workflows/header.yml b/.github/workflows/header.yml new file mode 100644 index 0000000000..e5c3359489 --- /dev/null +++ b/.github/workflows/header.yml @@ -0,0 +1,81 @@ +name: "header" +on: + workflow_dispatch: + push: + paths: + - "commons/**" + - "header/**" + pull_request: + paths: + - "commons/**" + - "header/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: "Setting up Java" + uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: "Configuring Maven" + run: | + mkdir -p $HOME/.m2 + cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Maven repository caching" + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: "Installing & checking formatting" + run: ./mvnw --no-transfer-progress -B -P-all,header -Dfmt.action=validate install -Dadditionalparam=-Xdoclint:none -DskipTests + + - name: "Running tests" + working-directory: header/ + run: ../mvnw --no-transfer-progress clean verify -Pit -Dfmt.skip=true -Dadditionalparam=-Xdoclint:none + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image" + if: github.repository == 'georchestra/georchestra' + working-directory: header/ + run: ../mvnw --no-transfer-progress clean package docker:build -Pdocker,log4j-logstash,sentry-log4j -DdockerImageName=georchestra/header:${{ steps.version.outputs.VERSION }} -DskipTests + + - name: "Logging in docker.io" + uses: azure/docker-login@v1 + if: github.repository == 'georchestra/georchestra' + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/header:${{ steps.version.outputs.VERSION }} georchestra/header:latest + docker push georchestra/header:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/header:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/header:${{ steps.version.outputs.VERSION }} + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.github/workflows/ldap.yml b/.github/workflows/ldap.yml new file mode 100644 index 0000000000..06f8b25814 --- /dev/null +++ b/.github/workflows/ldap.yml @@ -0,0 +1,49 @@ +name: "LDAP" +on: + workflow_dispatch: + push: + paths: + - "ldap/**" + pull_request: + paths: + - "ldap/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: Getting image tag + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image" + if: github.repository == 'georchestra/georchestra' + working-directory: ldap/ + run: docker build -t georchestra/ldap:${{ steps.version.outputs.VERSION }} . + + - name: "Logging in docker.io" + if: github.repository == 'georchestra/georchestra' + uses: azure/docker-login@v1 + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/ldap:${{ steps.version.outputs.VERSION }} georchestra/ldap:latest + docker push georchestra/ldap:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/ldap:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/ldap:${{ steps.version.outputs.VERSION }} diff --git a/.github/workflows/mapfishapp.yml b/.github/workflows/mapfishapp.yml new file mode 100644 index 0000000000..a1b64e7498 --- /dev/null +++ b/.github/workflows/mapfishapp.yml @@ -0,0 +1,76 @@ +name: "mapfishapp" +on: + workflow_dispatch: + push: + paths: + - "commons/**" + - "mapfishapp/**" + pull_request: + paths: + - "commons/**" + - "mapfishapp/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: "Checking out" + uses: actions/checkout@v2 + with: + submodules: true + + - name: "Setting up Java" + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '8' + cache: 'maven' + +# Could not transfer artifact org.springframework.boot:spring-boot-dependencies:pom:1.5.19.RELEASE from/to artifactory-georchestra +# - name: "Configuring Maven" +# run: | +# mkdir -p $HOME/.m2 +# cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Installing & checking formatting" + run: ./mvnw install -pl mapfishapp -am -ntp -B -P-all,mapfishapp -Dfmt.action=validate -Dadditionalparam=-Xdoclint:none -DskipTests + + - name: "Running tests" + run: ./mvnw verify -pl mapfishapp -P-all,mapfishapp -ntp -Dfmt.skip -Dadditionalparam=-Xdoclint:none + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image" + if: github.repository == 'georchestra/georchestra' + run: ./mvnw clean package -pl mapfishapp docker:build -ntp -P-all,mapfishapp,docker,log4j-logstash,sentry-log4j -DdockerImageName=georchestra/mapfishapp:${{ steps.version.outputs.VERSION }} -DskipTests + + - name: "Logging in docker.io" + if: github.repository == 'georchestra/georchestra' + uses: azure/docker-login@v1 + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/mapfishapp:${{ steps.version.outputs.VERSION }} georchestra/mapfishapp:latest + docker push georchestra/mapfishapp:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/mapfishapp:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/mapfishapp:${{ steps.version.outputs.VERSION }} + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.github/workflows/sp.yml b/.github/workflows/sp.yml new file mode 100644 index 0000000000..3ccfe55d95 --- /dev/null +++ b/.github/workflows/sp.yml @@ -0,0 +1,84 @@ +name: "security-proxy" +on: + workflow_dispatch: + push: + paths: + - "commons/**" + - "ldap-account-management/**" + - "security-proxy/**" + pull_request: + paths: + - "commons/**" + - "ldap-account-management/**" + - "security-proxy/**" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: "Checking out" + uses: actions/checkout@v1 + + - name: "Setting up Java" + uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: "Configuring Maven" + run: | + mkdir -p $HOME/.m2 + cp .github/resources/m2-settings.xml $HOME/.m2/settings.xml + + - name: "Maven repository caching" + uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: "Installing & checking formatting" + run: ./mvnw install -pl :security-proxy --also-make -P-all,security-proxy -DskipTests --no-transfer-progress -B -Dfmt.action=validate -Dadditionalparam=-Xdoclint:none + # note "-pl :security-proxy --also-make" builds only the project and its dependencies + + - name: "Running tests" + working-directory: security-proxy/ + run: ../mvnw verify -Pit --no-transfer-progress -Dfmt.skip=true -Dadditionalparam=-Xdoclint:none + + - name: Getting image tag + if: github.repository == 'georchestra/georchestra' + id: version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + + - name: "Building docker image" + if: github.repository == 'georchestra/georchestra' + working-directory: security-proxy/ + run: ../mvnw clean package docker:build -Pdocker,log4j-logstash,sentry-log4j -DdockerImageName=georchestra/security-proxy:${{ steps.version.outputs.VERSION }} -DskipTests --no-transfer-progress + + - name: "Logging in docker.io" + uses: azure/docker-login@v1 + if: github.repository == 'georchestra/georchestra' + with: + username: '${{ secrets.DOCKER_HUB_USERNAME }}' + password: '${{ secrets.DOCKER_HUB_PASSWORD }}' + + - name: "Pushing latest to docker.io" + if: github.ref == 'refs/heads/master' && github.repository == 'georchestra/georchestra' + run: | + docker tag georchestra/security-proxy:${{ steps.version.outputs.VERSION }} georchestra/security-proxy:latest + docker push georchestra/security-proxy:latest + + - name: "Pushing release branch to docker.io" + if: contains(github.ref, 'refs/heads/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/security-proxy:${{ steps.version.outputs.VERSION }} + + - name: "Pushing release tag to docker.io" + if: contains(github.ref, 'refs/tags/22.') && github.repository == 'georchestra/georchestra' + run: | + docker push georchestra/security-proxy:${{ steps.version.outputs.VERSION }} + + - name: "Remove SNAPSHOT jars from repository" + run: | + find .m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} diff --git a/.gitignore b/.gitignore index afafd01f4c..650ec582fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,10 @@ -*/target/ +**/target/ */jsbuild/venv/ */jsbuild/env/ -*/src/main/webapp/app/js/GEOR_custom.js -*/src/main/webapp/build/ -*/jsbuild/distribute-*.tar.gz -*/jsbuild/setuptools-*.egg -*/src/main/webapp/WEB-INF/jsp/header.jsp -PostTreatment.groovy **/.project **/.classpath **/.settings .idea/ *.iml +*~ +.attach_pid* diff --git a/.gitmodules b/.gitmodules index e874437f16..0dc20d17b1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,54 +1,24 @@ -[submodule "mapfishapp/src/main/webapp/lib/externals/ext"] - path = mapfishapp/src/main/webapp/lib/externals/ext - url = https://github.com/probonogeek/extjs.git [submodule "mapfishapp/src/main/webapp/lib/externals/geoext"] path = mapfishapp/src/main/webapp/lib/externals/geoext url = https://github.com/georchestra/geoext.git + branch = georchestra-16.12 [submodule "mapfishapp/src/main/webapp/lib/externals/openlayers"] path = mapfishapp/src/main/webapp/lib/externals/openlayers url = https://github.com/georchestra/openlayers.git -[submodule "extractorapp/src/main/webapp/lib/externals/geoext"] - path = extractorapp/src/main/webapp/lib/externals/geoext - url = https://github.com/geoext/geoext.git -[submodule "extractorapp/src/main/webapp/lib/externals/openlayers"] - path = extractorapp/src/main/webapp/lib/externals/openlayers - url = https://github.com/georchestra/openlayers.git -[submodule "extractorapp/src/main/webapp/lib/externals/ext"] - path = extractorapp/src/main/webapp/lib/externals/ext - url = https://github.com/probonogeek/extjs.git -[submodule "extractorapp/src/main/webapp/admin/extjs"] - path = extractorapp/src/main/webapp/admin/extjs - url = https://github.com/probonogeek/extjs.git -[submodule "analytics/src/main/webapp/js/lib/external/ext"] - path = analytics/src/main/webapp/js/lib/external/ext - url = https://github.com/probonogeek/extjs.git + branch = georchestra-16.12 [submodule "geonetwork"] path = geonetwork url = https://github.com/georchestra/geonetwork.git -[submodule "geoserver/georchestra-geoserver/geoserver-checkouts/geoserver-stable"] - path = geoserver/georchestra-geoserver/geoserver-checkouts/geoserver-stable - url = https://github.com/geoserver/geoserver.git -[submodule "catalogapp/src/main/webapp/lib/externals/ext"] - path = catalogapp/src/main/webapp/lib/externals/ext - url = https://github.com/probonogeek/extjs.git -[submodule "catalogapp/src/main/webapp/lib/externals/openlayers"] - path = catalogapp/src/main/webapp/lib/externals/openlayers - url = https://github.com/openlayers/openlayers.git -[submodule "catalogapp/src/main/webapp/lib/externals/geoext"] - path = catalogapp/src/main/webapp/lib/externals/geoext - url = https://github.com/geoext/geoext.git -[submodule "geoserver/geoserver-submodule"] - path = geoserver/geoserver-submodule - url = https://github.com/georchestra/geoserver.git + branch = georchestra-gn4-4.x [submodule "mapfishapp/src/main/webapp/lib/externals/styler"] path = mapfishapp/src/main/webapp/lib/externals/styler url = https://github.com/georchestra/styler.git -[submodule "config/configurations/template"] - path = config/configurations/template - url = https://github.com/georchestra/template.git -[submodule "geoserver/geofence"] - path = geoserver/geofence - url = https://github.com/georchestra/geofence.git -[submodule "geotools"] - path = geotools - url = https://github.com/georchestra/geotools.git + branch = master +[submodule "mapfishapp/src/main/webapp/lib/externals/ext"] + path = mapfishapp/src/main/webapp/lib/externals/ext + url = https://github.com/georchestra/extjs.git + branch = 3.x +[submodule "geoserver/geoserver-submodule"] + path = geoserver/geoserver-submodule + url = https://github.com/georchestra/geoserver.git + branch = 2.18.3-georchestra diff --git a/.mvn/formatter.xml b/.mvn/formatter.xml new file mode 100644 index 0000000000..44ddbfc309 --- /dev/null +++ b/.mvn/formatter.xml @@ -0,0 +1,354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000000..c32394f140 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.5"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000..0d5e649888 Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000..fa87ad7ddf --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.1/apache-maven-3.6.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 14d9208640..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: java -sudo: false -git: - depth: 1 - submodules: true -before_install: - - wget http://build.georchestra.org/m2.tar - - tar xvf m2.tar -C ~ > /dev/null -notifications: - irc: - channels: - - irc.freenode.net#georchestra - template: - - "%{repository}#%{build_number} (%{branch}) %{message} %{build_url}" -install: mvn install -DskipTests -P-all,travis -script: mvn integration-test -P-all,travis -addons: - apt: - packages: - - ant - - ant-optional - - python-virtualenv - - python-pip \ No newline at end of file diff --git a/AUTHORS.txt b/AUTHORS.txt new file mode 100644 index 0000000000..7d47b7de84 --- /dev/null +++ b/AUTHORS.txt @@ -0,0 +1,29 @@ +Authors approximately ordered by first contribution + +Jesse Eichar +Eric Lemoine +Pierre Mauduit +François Prunayre +François Van Der Biest +Yoann Buch +Bruno Binet +Pierre Giraud +Fabrice Phung +Philippe Huet +Yves Jacolin +Lydie Vinsonneau +Florent Gravin +Sylvain Lesage +Davis Mendoza +Mauricio Pazos +Sébastien Pelhate +Stéphane Brunner +Landry Breuil +Jeremy Kalsron +Julien Sabatier +Jean-Pascal Klipfel +Louca Lerch +Ernest Chiarello +Julien Acroute +Vincent Dorut +Jean-Denis Giguère diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..231915f042 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at psc@georchestra.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7e9317795e..8cdfe8e6a6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,27 +1,32 @@ # Contributing -First of all: thanks for contributing! +First of all: thanks for contributing! +We welcome all contributions. ## Code conventions -* Javascript: +* Javascript: * Four space indents. * No tabs. * Always use brackets after a test even if there's a single line of code. -* Java: +* Java: * Tab indents. * No spaces. * No trailing spaces or tabs. -* XML: - * TODO ## Commits policy -* commits must be atomic: just one consistent change per commit -* when possible, refer to an issue in the commit message, for example adding +* Commits must be atomic: just one consistent change per commit. +* When possible, refer to an issue in the commit message, for example adding `(see #xx)` at the end of the commit message, or `fixes ##` to close the issue. -* never commit environnement related changes +* Never commit environnement related changes. ## Submitting a pull request -* keep pull requests as simple as possible. +* Keep pull requests as simple as possible. Remember: one PR targets one and only one feature or fix. +* A bugfix PR should target the oldest supported branch where the bug appears. Releases get bugfixes during one year. +* If a PR impacts the deployment procedure, it should also include documentation. +* If a PR requires the admin to update any database or file, it should also update the [release notes](RELEASE_NOTES.md). + +Failure to do so will result in longer acceptance time. +In addition, PRs which have not been updated by their author 1 year after the latest comment might get closed. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..271baa3876 --- /dev/null +++ b/Makefile @@ -0,0 +1,109 @@ +# Docker related targets + +GEOSERVER_EXTENSION_PROFILES=colormap,mbtiles,wps-download,app-schema,control-flow,csw,inspire,libjpeg-turbo,monitor,pyramid,wps,css,s3-geotiff,jp2k,authkey,mapstore2,mbstyle,ogcapi,web-resource,flatgeobuf,sldservice +BTAG=latest + +docker-pull-jetty: + docker pull jetty:9-jre8 + +docker-build-ldap: + docker pull debian:buster + cd ldap; \ + docker build -t georchestra/ldap:${BTAG} . + +docker-build-database: + docker pull postgres:13 + cd postgresql; \ + docker build -t georchestra/database:${BTAG} . + +docker-build-gn: docker-pull-jetty + mvn install -pl security-proxy-spring-integration --also-make -DskipTests; \ + cd geonetwork; \ + mvn -DskipTests clean install; \ + cd web; \ + mvn -P docker -DskipTests package docker:build -DdockerImageTags=${BTAG} + +docker-build-geoserver: docker-pull-jetty + cd geoserver; \ + LANG=C mvn clean install -DskipTests -Dfmt.skip=true -P${GEOSERVER_EXTENSION_PROFILES}; \ + cd webapp; \ + mvn clean install docker:build -DdockerImageTags=${BTAG} -Pdocker,${GEOSERVER_EXTENSION_PROFILES} -DskipTests + +docker-build-geoserver-geofence: docker-pull-jetty + cd geoserver; \ + LANG=C mvn clean install -DskipTests -Dfmt.skip=true -Pgeofence-server,${GEOSERVER_EXTENSION_PROFILES} ; \ + cd webapp; \ + mvn clean install docker:build -DdockerImageTags=${BTAG} -Pdocker,geofence,${GEOSERVER_EXTENSION_PROFILES} -DskipTests + +docker-build-geowebcache: docker-pull-jetty + mvn clean package docker:build -DdockerImageTags=${BTAG} -Pdocker -DskipTests -pl geowebcache-webapp + +docker-build-proxy: build-deps docker-pull-jetty + mvn clean package docker:build -DdockerImageTags=${BTAG} -Pdocker -DskipTests --pl security-proxy + +docker-build-console: build-deps docker-pull-jetty + mvn clean package docker:build -DdockerImageTags=${BTAG} -Pdocker -DskipTests --pl console + +docker-build-analytics: build-deps docker-pull-jetty + mvn clean package docker:build -DdockerImageTags=${BTAG} -Pdocker -DskipTests --pl analytics + +docker-build-mapfishapp: build-deps docker-pull-jetty + mvn clean package docker:build -DdockerImageTags=${BTAG} -Pdocker -DskipTests --pl mapfishapp + +docker-build-datafeeder: build-deps + mvn clean package docker:build -DdockerImageTags=${BTAG} -Pdocker -DskipTests --pl datafeeder + +docker-build-georchestra: build-deps docker-pull-jetty docker-build-database docker-build-ldap docker-build-geoserver docker-build-geowebcache docker-build-gn + mvn clean package docker:build -DdockerImageTags=${BTAG} -Pdocker -DskipTests --pl extractorapp,security-proxy,mapfishapp,header,console,analytics,atlas,datafeeder + +docker-build: docker-build-gn docker-build-geoserver docker-build-georchestra + + +# WAR related targets + +war-build-geoserver: build-deps + cd geoserver; \ + LANG=C mvn clean install -DskipTests -Dfmt.skip=true -P${GEOSERVER_EXTENSION_PROFILES}; \ + cd ..; \ + mvn clean install -pl geoserver/webapp -P${GEOSERVER_EXTENSION_PROFILES} + +war-build-geoserver-geofence: build-deps + cd geoserver; \ + LANG=C mvn clean install -DskipTests -Dfmt.skip=true -P${GEOSERVER_EXTENSION_PROFILES},geofence; \ + cd ..; \ + mvn clean install -pl geoserver/webapp -P${GEOSERVER_EXTENSION_PROFILES} + +war-build-geowebcache: build-deps + mvn clean install -pl geowebcache-webapp -DskipTests -Dfmt.skip=true + +war-build-gn: build-deps + mvn clean install -pl testcontainers,ldap-account-management,security-proxy-spring-integration -DskipTests + mvn clean install -f geonetwork/pom.xml -DskipTests + +war-build-georchestra: war-build-gn war-build-geoserver + mvn -Dmaven.test.skip=true -DskipTests clean install + + +# DEB related targets + +deb-build-geoserver: war-build-geoserver + cd geoserver; \ + mvn clean package deb:package -pl webapp -PdebianPackage,${GEOSERVER_EXTENSION_PROFILES} ${DEPLOY_OPTS} + +deb-build-geoserver-geofence: war-build-geoserver-geofence + cd geoserver; \ + mvn clean package deb:package -pl webapp -PdebianPackage,geofence,${GEOSERVER_EXTENSION_PROFILES} ${DEPLOY_OPTS} + +deb-build-geowebcache: war-build-geowebcache + mvn package deb:package -pl geowebcache-webapp -PdebianPackage -DskipTests -Dfmt.skip=true ${DEPLOY_OPTS} + +deb-build-georchestra: war-build-georchestra build-deps deb-build-geoserver deb-build-geowebcache + mvn package deb:package -pl atlas,datafeeder,datafeeder-ui,security-proxy,header,mapfishapp,extractorapp,analytics,console,geonetwork/web -PdebianPackage -DskipTests ${DEPLOY_OPTS} + +# Base geOrchestra common modules +build-deps: + mvn -Dmaven.test.failure.ignore clean install --non-recursive + mvn clean install -pl commons,ogc-server-statistics -Dmaven.javadoc.failOnError=false + +# all +all: war-build-georchestra deb-build-georchestra docker-build diff --git a/PATCHES.md b/PATCHES.md deleted file mode 100644 index 0902a541db..0000000000 --- a/PATCHES.md +++ /dev/null @@ -1,48 +0,0 @@ -The goal of this file is to keep an up-to-date list of patches to the base libraries and software used across the application. - -Mapfishapp -========== - -* OpenLayers - * georchestra-13.06 branch - based on https://github.com/openlayers/openlayers/commit/fe5b8a7a78f46c41e09233b0f3bba5cd35d553b0 - * [MERGED] https://github.com/openlayers/openlayers/pull/1153 - Handle gutter value in WMC - * [MERGED] https://github.com/openlayers/openlayers/pull/763 - WFST 1.0.0 InsertResult not read correctly - * [MERGED] https://github.com/openlayers/openlayers/pull/760 - Correct handling of 0 and Infinity ScaleHint - * https://github.com/openlayers/openlayers/pull/901 - transitionEffect support in WMC format - * georchestra-13.09 branch - based on https://github.com/openlayers/openlayers/commit/e39a5d843500691830f1f435fde9e1212dacbc54 - * [MERGED] https://github.com/openlayers/openlayers/pull/1153 - Handle gutter value in WMC - * [MERGED] https://github.com/openlayers/openlayers/pull/1151 - Fix dependencies for WFS v2 - * https://github.com/openlayers/openlayers/pull/901 - transitionEffect support in WMC format - * georchestra-13.12 branch - based on https://github.com/openlayers/openlayers/commit/e39a5d843500691830f1f435fde9e1212dacbc54 - * [MERGED] https://github.com/openlayers/openlayers/pull/1182 - Handle the full layer attribution information - * [MERGED] https://github.com/openlayers/openlayers/pull/1153 - Handle gutter value in WMC - * [MERGED] https://github.com/openlayers/openlayers/pull/1151 - Fix dependencies for WFS v2 - * https://github.com/openlayers/openlayers/pull/901 - transitionEffect support in WMC format -* GeoExt - * georchestra-13.06 - based on https://github.com/georchestra/geoext/commit/142ac6fd59d8fbdbc7a6dc274ea81eeeebe3ea9d - * [MERGED] https://github.com/geoext/geoext/pull/18 - Fixing legend url with custom format. - * georchestra-13.09 - based on https://github.com/geoext/geoext/commit/442b358998e2a0074c0b2c7ea9c0f58e3d9a902f - * https://github.com/geoext/geoext/pull/96 - WMS print encoder supports WMS 1.3.0 - * georchestra-13.12 - based on https://github.com/geoext/geoext/commit/d092615a8ae14acdd3899dabe3ff94969fa9adc4 - * https://github.com/geoext/geoext/pull/96 - WMS print encoder supports WMS 1.3.0 - - -Extractorapp -============ - -TODO - -Catalogapp -========== - -TODO - -GeoServer -========= - -TODO - -GeoNetwork -========== - -TODO \ No newline at end of file diff --git a/README.md b/README.md index 6553306ef9..bb1030d82d 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,9 @@ # geOrchestra -[![Build Status](https://travis-ci.org/georchestra/georchestra.svg?branch=master)](https://travis-ci.org/georchestra/georchestra) -geOrchestra is a complete **Spatial Data Infrastructure** solution. - -It features a **metadata catalog** (GeoNetwork 2.10), an **OGC server** (GeoServer 2.5.4 and GeoWebCache 1.5.4) with fine-grained access control (based on GeoFence), an **advanced viewer and editor**, an **extractor** and **many more** (security and auth system based on proxy/CAS/LDAP, analytics, admin UIs, ...) - -## Releases - -A new release is published every 6 months and is supported during 12 months. Stable versions are named by their release date, eg 14.06 was published in June 2014. - -Before downloading, you might be interested in the [release notes](RELEASE_NOTES.md) and the [kanban board](https://huboard.com/georchestra/georchestra) we're using to manage issues. - -## Download - -To download the latest stable version (currently 15.06), use the following command line: -``` -git clone --recursive https://github.com/georchestra/georchestra.git ~/georchestra -``` +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/cd93d7fdbd3a44e48991f3146251c79e)](https://www.codacy.com/gh/georchestra/georchestra/dashboard?utm_source=github.com&utm_medium=referral&utm_content=georchestra/georchestra&utm_campaign=Badge_Grade) -## Install - -To install geOrchestra, you will have to: - * [create your own configuration repository](doc/config.md), based on the [template](https://github.com/georchestra/template) we provide, - * [build the web applications](doc/build.md) with your config, - * [setup the middleware](doc/setup.md) (apache, tomcat, postgresql, openldap), - * [deploy the webapps](doc/deploy.md), [check](doc/check.md) they're working as expected and finally [configure](doc/post-deploy_config.md) them. - -There are also several [optimizations](doc/optimizations.md), [good practices](doc/good_practices.md) and [tutorials](doc/tutorials.md) that are worth reading. - -The minimum system requirement is 2 cores and 4Gb RAM, but we recommend at least 4 cores and 8 Gb RAM for a production instance. -More RAM is of course better ! - -## Community - -If you need more information, please ask on the [geOrchestra mailing list](https://groups.google.com/forum/#!forum/georchestra). - -For help setting up your instance, or for dev-related questions, use the [#georchestra](https://kiwiirc.com/client/irc.freenode.net/georchestra) IRC channel or the [dev/tech list](https://groups.google.com/forum/#!forum/georchestra-dev). +geOrchestra is a complete **Spatial Data Infrastructure** solution. -## More +It features a **metadata catalog** (GeoNetwork), an **OGC server** (GeoServer) with fine-grained access control (based on GeoFence), an **advanced viewer and editor**, an **extractor** and **many more** (security and auth system based on proxy/CAS/LDAP, analytics, admin UIs, ...) -Additional information can be found in the [georchestra.org](http://www.georchestra.org/) website and in the following links: - * [catalog](https://github.com/georchestra/geonetwork/blob/georchestra-15.06/README.md): standard GeoNetwork with a light customization, - * [viewer](mapfishapp/README.md) (aka mapfishapp): a robust, OGC-compliant webgis with editing capabilities, - * [extractor](extractorapp/README.md) (aka extractorapp): able to create zips from data served through OGC web services and send an email when your extraction is done, - * [geoserver](http://geoserver.org/): the reference implementation for many OGC web services, - * [geowebcache](http://geowebcache.org/): a fast and easy to use tile cache, - * [geofence](https://github.com/georchestra/geofence/blob/georchestra/georchestra.md): optional, advanced OGC web services security, - * [simple catalog](catalogapp/README.md) (aka catalogapp): a very lightweight UI to query CSW services, - * [analytics](analytics/README.md): admin-oriented module, a front-end to the [ogc-server-statistics](ogc-server-statistics/README.md) and [downloadform](downloadform/README.md) modules, - * [ldapadmin](ldapadmin/README.md): also an admin-oriented module, to manage users and groups, - * [header](header/README.md): the common header which is used by all modules, - * [epsg-extension](epsg-extension/README.md): a plugin to override the geotools srs definitions. +Please refer to the [documentation](docs/index.md) for more information. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md deleted file mode 100644 index 6bfd4f450b..0000000000 --- a/RELEASE_NOTES.md +++ /dev/null @@ -1,838 +0,0 @@ -The development branch is master. It can be used for testing and reporting errors. - -For production systems, you are advised to use the stable branch (currently 15.06). -This branch receives bug fixes as they arrive, during 6 months at least. - -Version 15.12 -============= - - - -### UPGRADING: - * As a result of [#1040](https://github.com/georchestra/georchestra/pull/1040), LDAP groups are now ```groupOfMembers``` instances rather than ```groupOfNames``` instances. In addition, the ```PENDING_USERS``` group was renamed. You have to migrate your LDAP tree, according to the following procedure (please change the ```dc=georchestra,dc=org``` string for your own base DN and provide a suitable password): - * dump your ldap **groups** with: - ``` - ldapsearch -H ldap://localhost:389 -xLLL -D "cn=admin,dc=georchestra,dc=org" -w your_ldap_password -b "ou=groups,dc=georchestra,dc=org" > /tmp/groups.ldif - ``` - * migration: - ``` - sed -i 's/PENDING_USERS/PENDING/' /tmp/groups.ldif - sed -i 's/groupOfNames/groupOfMembers/' /tmp/groups.ldif - sed -i '/fakeuser/d' /tmp/groups.ldif - ``` - * load the groupOfMembers definition: - ``` - wget --no-check-certificate https://raw.githubusercontent.com/georchestra/LDAP/YY.MM/georchestra-groupofmembers.ldif -O /tmp/groupofmembers.ldif - sudo ldapadd -Y EXTERNAL -H ldapi:/// -f /tmp/groupofmembers.ldif - ``` - * drop your groups organizationalUnit (```ou```) - * import the updated groups.ldif file. - - -Version 15.06 -============= - -This release brings several key enhancements: - * support for the newer Debian 8 "Jessie", including tomcat 7, 8 & apache 2.4, - * standard geoserver artifacts can now be used instead of the "geOrchestra flavoured geoserver", - * [travis-ci](https://travis-ci.org/georchestra/georchestra) service integration, with improved test coverage, - * lighter artifacts. - -Other enhancements include: - * mapfishapp: redirect to iD as OSM editor - see [#992](https://github.com/georchestra/georchestra/issues/992), - * mapfishapp: gml & kml export of results - see [#995](https://github.com/georchestra/georchestra/pull/995), - * mapfishapp: the admin may restrict all metadata searches to a specific extent, while the user may now restrict his search to the current map extent - see [#964](https://github.com/georchestra/georchestra/pull/964), - * mapfishapp: added possibility to filter visible features in POSTed layers with CQL - see [#921](https://github.com/georchestra/georchestra/pull/921), - * proxy: intercepts "connection refused" errors - see [#963](https://github.com/georchestra/georchestra/pull/963), - * doc: added SASL configuration for remote AD - see [#958](https://github.com/georchestra/georchestra/pull/958). - -Full list of issues on GitHub issue tracker, tagged with milestone [15.06](https://github.com/georchestra/georchestra/issues?q=milestone%3A15.06). - -Please note that: - * the protocol between the security proxy and the apps was slightly modified as a result of [#920](https://github.com/georchestra/georchestra/issues/920). This means that the proxy and webapps from this release are not backward-compatible: you cannot use mapfishapp from 15.06 with a 14.12 security-proxy. - * geOrchestra 14.01 is not supported anymore. - -Many thanks to all contributors for this great release ! - - -Version 14.12 -============= - -This release received contributions from the GeoBretagne, GéoPicardie, CIGALsace, CRAIG, Rennes Métropole, Le Puy en Velay and Université de Franche-Comté projects. -It comes with an improved [install documentation](README.md). - -According to our release policy, geOrchestra 13.09 is not supported anymore. - -New features: - * extractorapp: native raster resolution extracted by default - see [#726](https://github.com/georchestra/georchestra/issues/726), - * geofence: ability to configure the map SRS - see [#732](https://github.com/georchestra/georchestra/issues/732), - * geoserver: updated to version 2.5.4 with geofence - see [#677](https://github.com/georchestra/georchestra/issues/677), - * ldapadmin: identify users not members of a SV_* group - see [#834](https://github.com/georchestra/georchestra/issues/834), - * mapfishapp: context restoring - option to reset map or not - see [#302](https://github.com/georchestra/georchestra/issues/302), - * mapfishapp: spatial query using previous results' geometries - see [#752](https://github.com/georchestra/georchestra/issues/752), - * mapfishapp: streetview addon - see [#774](https://github.com/georchestra/georchestra/issues/774), - * mapfishapp: direct geodata file loading via URL - see [#754](https://github.com/georchestra/georchestra/issues/754), - * mapfishapp: added a button to remove all layers - see [#753](https://github.com/georchestra/georchestra/issues/753), - * mapfishapp: new syntax for CSW querier - see [#760](https://github.com/georchestra/georchestra/issues/760) and the [README](https://github.com/georchestra/georchestra/blob/master/mapfishapp/README.md#cswquerier), - * mapfishapp: png printing - see [#814](https://github.com/georchestra/georchestra/issues/814), - * mapfishapp: print extent handling improvement - see [#813](https://github.com/georchestra/georchestra/issues/813), - * mapfishapp: give easy access to WMS/WFS layer details - see [#346](https://github.com/georchestra/georchestra/issues/346), - * mapfishapp: export current map context as a "map metadata" - see [#751](https://github.com/georchestra/georchestra/issues/751), - * mapfishapp: revamp of the "add layers" window - see [#718](https://github.com/georchestra/georchestra/issues/718), - * mapfishapp: help message popping down when tools are loaded and available, - * mapfishapp: if filled, wmc title and abstract are now displayed on context restored - see [#816](https://github.com/georchestra/georchestra/issues/816), - * mapfishapp: filter contexts by keywords - see [#866](https://github.com/georchestra/georchestra/issues/866). - -Enhancements: - * extractorapp: editable combobox for resolution selection - see [#726](https://github.com/georchestra/georchestra/issues/726), - * extractorapp: warn user when the extraction area is too large before the extraction is fired - see [#726](https://github.com/georchestra/georchestra/issues/726), - * extractorapp: better emails - see [#750](https://github.com/georchestra/georchestra/issues/750), - * ldapadmin: test suite improved - see [#843](https://github.com/georchestra/georchestra/pull/843), - * mapfishapp: print extent fully visible, - * mapfishapp: print templates improved - see [#17](https://github.com/georchestra/template/pull/17), - * mapfishapp: geometry from query stored forever in browser/LocalStorage (rather than cookie), - * mapfishapp: warn users about chrome 36/37 issue, - * mapfishapp: filter out WFS services not supporting POST, - * mapfishapp: i18n neutral nopreview image for csw querier - see [#775](https://github.com/georchestra/georchestra/issues/775), - * mapfishapp: load data from catalog improvement - see [#756](https://github.com/georchestra/georchestra/issues/756), - * mapfishapp: advanced csw querier syntax help - see [#478](https://github.com/georchestra/georchestra/issues/478), - * mapfishapp: addons can be placed in toolbars now - see [#132](https://github.com/georchestra/georchestra/issues/132), - * proxy: handle a list of servers for which the x-forwarded headers should not be sent (typically geo.admin.ch) - see [#782](https://github.com/georchestra/georchestra/issues/782), - * georchestra: german translations generalized - see [#777](https://github.com/georchestra/georchestra/issues/777), - * georchestra: the maven build is now aware of the javascript build outcome - see [#809](https://github.com/georchestra/georchestra/issues/809). - -Bug fixes: - * analytics: fixed global csv export - see [#835](https://github.com/georchestra/georchestra/issues/835), - * analytics: fixed log4j config issue - see [#785](https://github.com/georchestra/georchestra/issues/785), - * analytics: fixed broken double click to see details - see [#883](https://github.com/georchestra/georchestra/issues/883), - * build: fixed by migrating from OpenGeo to Boundless repository and also by setting up our own repository, - * cas: fixed "after logging in from /cas/login the header does not show the connected user" - see [#837](https://github.com/georchestra/georchestra/issues/837), - * catalogapp: fixed missing log4j configuration file - see [#861](https://github.com/georchestra/georchestra/issues/861), - * config: fixed proxy targets wrong connector for geoserver - see [c2dca7](https://github.com/georchestra/template/commit/c2dca72c647e9e33662655232212601fb5f5ac45), - * epsg-extension: fixed the EPSG:27572 SRS for use in mapfishapp and extractorapp - see [#379](https://github.com/georchestra/georchestra/issues/379), - * extractorapp: workaround a geoserver issue for EPSG:4326 bboxes, - * extractorapp: fixed WCS specific regexp in checkPermission method, - * geonetwork: fixed LDAP sync - see [6a7e69](https://github.com/georchestra/geonetwork/commit/6a7e692daaabe3b27793b4c75de2bc91ffe43840), - * geonetwork: in associated resource panel from editor, fixed failure to add link to service metadata record, - * geonetwork: schema iso / fra - fixed missing description for "funder", - * ldapadmin: fixed renaming group name leads to corrupted LDAP entry - see [#810](https://github.com/georchestra/georchestra/issues/810), - * ldapadmin: fixed unescaped quotes in translations - see [#890](https://github.com/georchestra/georchestra/issues/890), - * mapfishapp: fixed js error on vector layer selection - see [#785](https://github.com/georchestra/georchestra/issues/785), - * mapfishapp: fixed missing SLD_VERSION WMS parameter, which is mandatory from version 1.3.0 on - see [#636](https://github.com/georchestra/georchestra/issues/636), - * mapfishapp: modified MAP_DOTS_PER_INCH value in order to fix WMS/WMTS layer overlay - see [#736](https://github.com/georchestra/georchestra/issues/736), - * mapfishapp: fixed WMTS style switch for GeoWebCache versions >= 1.5, - * mapfishapp: fixed WMTS legend printing issue, - * mapfishapp: workaround for IE11 bug, see [#773](https://github.com/georchestra/georchestra/issues/773), - * mapfishapp: fixed several programming errors in the cadastre addon, - * mapfishapp: restore record opaque status from layer transitionEffect, - * mapfishapp: fixed the csw queryable "Type" case, - * mapfishapp: fixed erroneous area measurements - see [#838](https://github.com/georchestra/georchestra/issues/838), - * mapfishapp: fixed spanish lang file formatting - see [043ef1](https://github.com/georchestra/georchestra/commit/043ef1eeee0865a3e4028cc1509ae5d9f4526bd9), - * proxy: fixed a vulnerability where client could access localhost resources via proxy url, see [5c9b4d](https://github.com/georchestra/georchestra/commit/5c9b4db1a8c004a582d2be4f2a909c68843cad59), - * proxy: prevented the use of the ogcproxy in production (it is required for development purposes only), - * proxy: restored access to public layers through qgis, udig and arcgis clients - see [b077f8](https://github.com/georchestra/georchestra/commit/b077f8e6695ea807d18342b35c63c1843f457ee0), - * proxy: checkhealth now targets the main geOrchestra database by default, see [a1d825](https://github.com/georchestra/template/commit/a1d825ecdda2c1f89ec78039d95ab17ba2f47310), - * proxy: force preemptive basic auth for GeoFence client to GeoServer REST - see [2c2b74](https://github.com/georchestra/georchestra/commit/2c2b74b5bf813e184a87e032c07cc4276a3f66a8), - * server-deploy: fixed wrong webapp names (geoserver-webapp, geofence-webapp, geowebcache-webapp), - * georchestra: many small fixes related to HTTPS support, eg [#745](https://github.com/georchestra/georchestra/issues/745), [#840](https://github.com/georchestra/georchestra/issues/840) and [#780](https://github.com/georchestra/georchestra/issues/780), - * georchestra: css3 border-radius property replaces browser (moz and webkit) implementations. - -### UPGRADING: - -As said previously, the [documentation](README.md) was improved in order to reflect the most recent project changes. -Most notably, it is now in line with the "3 tomcats"-based setup that drives the [template configuration](https://github.com/georchestra/template) since geOrchestra 14.06. -It also includes an interesting "[how to upgrade your config](doc/how_to_upgrade.md)" generic guide (that you should read, and maybe contribute to !). - -**config changes** - -We introduced a new global config option: ```shared.url.scheme``` which defaults to http. -Set to https (along with ```shared.server.port``` to 443) if your SDI requires it. Do not forget to also change the base url of GeoServer and GeoNetwork in their respectives admin GUI. - -In mapfishapp's GEOR_custom.js: - * the ```WMS_SERVERS```, ```WMTS_SERVERS```, ```WFS_SERVERS``` config options have been removed. The server definitions are now loaded via separate XHR's. You should migrate your content into the newly introduced ```myprofile/mapfishapp/*.json``` files. - * ```CONTEXT_LOADED_INDICATOR_DURATION``` was added to handle the "context loaded" popping down indicator duration. It defaults to 5 seconds. Set to 0 to disable the indicator. - * ```CONTEXTS``` was an array of arrays. It is now an array of objects. - -Eg, in 14.06: -```js -[ - ["OpenStreetMap", "app/img/contexts/osm.png", "default.wmc", "A unique OSM layer"], - [...] -] -``` - -From 14.12 on: -```js -[{ - label: "OpenStreetMap", - thumbnail: "app/img/contexts/osm.png", - wmc: "default.wmc", - tip: "A unique OSM layer", - keywords: ["OpenStreetMap", "Basemap"] -}, { - ... -}] -``` - -Also, the print templates have been improved. -If you made changes to the previous templates, you have to migrate them, or you may also keep your older templates. - -In extractorapp's GEOR_custom.js, several new javascript config options have been added, related to [#726](https://github.com/georchestra/georchestra/issues/726): ```SUPPORTED_RESOLUTIONS```, ```DEFAULT_RESOLUTION```, ```METADATA_RESOLUTION_XPATH```. Make sure your configuration is up to date with the template configuration, or you will get these variable defaults. - -Note also the addition of an ```excluded``` directory in the template configuration. The content of this directory will be ignored when creating the configuration jar, which is deployed in each webapp. This is a convenient way to store scripts and so on, versioned with your configuration. - -**new repositories** - -We decided to publish resources for your server "htdocs" folder. -Have a look at our [georchestra/htdocs](https://github.com/georchestra/htdocs) repository to get some inspiration. - -As you may know, since geOrchestra 14.06, we recommend to start from our [minimal GeoServer "data dir"](https://github.com/georchestra/geoserver_minimal_datadir), rather than using GeoServer's default. -For the 14.12 release, we also decided to publish a [minimal GeoNetwork data dir](https://github.com/georchestra/geonetwork_minimal_datadir) too ! - -**apache configuration** - -In geOrchestra's security proxy, there's an OGC proxy which we use to circumvent browser's [same origin policy](http://en.wikipedia.org/wiki/Same-origin_policy). -To prevent anyone to use this proxy (see [#755](https://github.com/georchestra/georchestra/issues/755)), we recommend to restrict access to the proxy by checking the request [Referer header](http://en.wikipedia.org/wiki/HTTP_referer), eg for apache <= 2.2 with: - -``` -SetEnvIf Referer "^http://my\.sdi\.org/" mysdi - - Order deny,allow - Deny from all - Allow from env=mysdi - -``` - -There's also another improvement when your SDI is not globally accessed through https: securing your communications to the ldapadmin webapp through the following: -``` -RewriteCond %{HTTPS} off -RewriteCond %{REQUEST_URI} ^/ldapadmin/?.*$ -RewriteRule ^/(.*)$ https://%{SERVER_NAME}/$1 [R=301,L] -``` -This can also be used to secure other modules, eg analytics, downloadform... - -**building** - -If you experience build issues, please clear your local maven repository (rm -rf ~/.m2/repository/), then try again. - -Our [continuous integration process](https://sdi.georchestra.org/ci/job/georchestra-template/) now checks every day that all geOrchestra modules (including GeoFence) build smoothly. -We also make sure that the following geoserver extensions are compatible: app-schema, authkey, charts, control-flow, css, csw, dds, dxf, feature-aggregate, feature-pregeneralized, geosearch, gdal, imagemap, inspire, istyler, kml, libjpeg-turbo, mysql, ogr, pyramid, script, spatialite, xslt, wps, w3ds. They can be integrated in your geoserver deployment by adding them in the build command line, eg with -``` -./mvn -Dserver=template -Dmaven.test.skip=true -Pgeofence -Pcontrol-flow,css,csw,gdal,inspire,kml,libjpeg-turbo,ogr,pyramid,spatialite,wps,w3ds clean install -``` - - -Version 14.06 -============== - -Contributions from Rennes, CIGAL, GeoBretagne, GeoPicardie, PIGMA, GeoBolivia, ViennAgglo & developers on their free time. -Note on the 13.06 release: end-of-life was in april, earlier this year. As a result, it will not receive bug fixes anymore. - -New features: - * GeoWebCache standalone is now part of the geOrchestra suite - -Enhancements: - * analytics: access is now restricted to members of the ```MOD_ANALYTICS``` group **only**, - * cas: upgraded to latest CAS Server 4.0.0 release - see [#503](https://github.com/georchestra/georchestra/issues/503), - * cas: members of the ```PENDING_USERS``` group are not allowed to login anymore - see [#581](https://github.com/georchestra/georchestra/issues/581), - * cas: unmodified files where removed from template config, - * cas: username field autocompleted on browsers that support it, - * catalogapp: german translation, - * deploy: a default [deployment script](https://github.com/georchestra/template/blob/master/DeployScript.groovy) is now provided by the template config, - * downloadform: complete revamp, including more testing, see [#657](https://github.com/georchestra/georchestra/issues/657), - * extractorapp: now displays layers using the configured ```GLOBAL_EPSG``` srs, if they support it, - * extractorapp: much improved test coverage, - * extractorapp: access is now restricted to members of the ```MOD_EXTRACTORAPP``` group **only**, - * extractorapp admin: access is now restricted to members of the ```ADMINISTRATOR``` group **only**, - * extractorapp: german translation, - * extractorapp: the privileged user is allowed to use imp-username/imp-roles headers to impersonate an arbitrary user for security check, - * extractorapp: improved GDAL/OGR handling, - * extractorapp: proper reporting in case the OGRDataStore cannot be created, with an invitation to check the GDAL libraries setup, - * extractorapp: dropped support for geoserver 1.x, which results in better startup performance, see [#640](https://github.com/georchestra/georchestra/issues/640), - * extractorapp: improved db connection pooling wrt download form, - * extractorapp admin: includes the standard geOrchestra header - see [#729](https://github.com/georchestra/georchestra/issues/729), - * geonetwork: rolling logs + gzipping - see [#200](https://github.com/georchestra/georchestra/issues/200), - * geonetwork: improved IE10 compatibility, - * geonetwork: disable user management for non-administrators, - * geonetwork: improved links to WMS and WMC got from ISO19139 records, "distribution" section - see [5e7671](https://github.com/georchestra/geonetwork/commit/5e7671e5c498cc18f0150dd33e5b123553161453) and [c53e77](https://github.com/georchestra/geonetwork/commit/c53e7748938fb82afc2786614b228175172ad744), - * geonetwork: added the possibility to search on a field and suggest on another one - see [4a3a34](https://github.com/georchestra/geonetwork/commit/4a3a34b031f2e1ceeec860e7cb5bb162da76fafa), - * geonetwork: improved iso19139.fra schema - see [62057e](https://github.com/georchestra/geonetwork/commit/62057e5ce6db4cfaafd67c288df20c1b7511ceeb), [fd086f](https://github.com/georchestra/geonetwork/commit/fd086f69161032f5fdfa34d2f29e90dfec0b2097) and [0eba33](https://github.com/georchestra/geonetwork/commit/0eba333269bce331e46f79c8a461f822544f0ec0), - * geoserver: the project now provides a [basic data_dir](https://github.com/georchestra/geoserver_minimal_datadir) with sensible defaults for geOrchestra - see [INSTALL.md#geoserver](INSTALL.md#geoserver), - * geofence: performance improvement for GetCapabilities requests, - * geofence: allows dynamic styles and remote layers by default, - * geofence: conforms to the global logging strategy as set by shared maven filters, - * geofence: allow [dynamic geofencing](https://github.com/georchestra/geofence/blob/georchestra/georchestra.md#static--dynamic-geofencing) based on inetOrgPerson's "l" field storing the limiting EWKT geometry, - * header: only members of the ```MOD_EXTRACTORAP``` group see the link to the extractor - see [#717](https://github.com/georchestra/georchestra/issues/717), - * ldapadmin: improved compatibility with geofence, by automatic management of unique numeric identifiers for users and groups, - * ldapadmin: SSHA password encryption, - * ldapadmin: much improved test coverage, - * ldapadmin: now takes into account shared.ldap.* values, - * ldapadmin: now uses configured LDAP users and groups DN instead of hardcoded values, - * ldapadmin: public context path now set via a shared maven filter - see [#721](https://github.com/georchestra/georchestra/issues/721) - * ldapadmin: hyphens are now allowed in user identifiers - see [#725](https://github.com/georchestra/georchestra/pull/725), - * mapfishapp: improved testsuite coverage, - * mapfishapp: annotation addon improved (outline and fill colors added), - * mapfishapp: using adequate XML exception format string, depending on WMS version, - * mapfishapp: max_features and max_length set to higher numbers, - * mapfishapp: german translation, including for the supported addons (thanks to CIGALsace), - * mapfishapp: display SLD title before its name - see [#597](https://github.com/georchestra/georchestra/issues/597), - * mapfishapp: file upload is now using a pure JSON implementation, - * mapfishapp: file upload checks that a SRS can be guessed by OGRFeatureReader, or else falls back onto the pure GeoTools implementation, - * mapfishapp: file upload supports OSM files to some extent, - * mapfishapp: proj4js definitions are now local to prevent mixed active content warning in case of HTTPS access, see [#602]( https://github.com/georchestra/georchestra/issues/602), - * mapfishapp: only members of the ```MOD_EXTRACTORAPP``` group see the "download data" link - see [#717](https://github.com/georchestra/georchestra/issues/717) - * mapfishapp: the "remove layer" icon was changed - see [#728](https://github.com/georchestra/georchestra/issues/728) - * server-deploy: now supports deploying geofence-webapp, - * all modules now use the same "georchestra" database by default, including GeoNetwork - see [#601](https://github.com/georchestra/georchestra/issues/601), - * system wide favicon. - -Bug fixes: - * extractorapp: fixed several client-side issues regarding handling of namespaced layer names and virtual services - see [#517](https://github.com/georchestra/georchestra/issues/517#issuecomment-40697504) and [#634](https://github.com/georchestra/georchestra/issues/634), - * extractorapp: fixed too early expiration of archive download link, - * extractorapp: fixed protected layer cannot be extracted - see [#633](https://github.com/georchestra/georchestra/issues/633), - * extractorapp: fixed KML export. Note: MIF export via OGR can only work with a patched version of GDAL, or GDAL >= 1.11.1, see [gdal#5477](http://trac.osgeo.org/gdal/ticket/5477), - * geofence: avoid NPE with dynamic geofencing - [#629](https://github.com/georchestra/georchestra/issues/629), - * geonetwork: shared.geonetwork.language enforced - see [#595](https://github.com/georchestra/georchestra/issues/595), - * geonetwork: keywords-based extent suggestion does not fail anymore with aceentuated chars - see [652e87](https://github.com/georchestra/geonetwork/commit/652e87c073e438d9b75150fc786c823f6c346467), - * geonetwork: fixed extent map cursor position after panel has been scrolled - see [95aa65](https://github.com/georchestra/geonetwork/commit/95aa659dc0def87dab33642b9d761d08c746700a), - * geonetwork: fixed classification of spatial data service for better INSPIRE compatibility - see [c1017d](https://github.com/georchestra/geonetwork/commit/c1017d41971219369ed2671c5d1132e6167c4e90), - * geonetwork: avoid JS error when the WMS layer title contains a quote - see [7bbc34](https://github.com/georchestra/geonetwork/commit/7bbc34c902b1390935799f93aa6e3f7d90edd092), - * ldapadmin: fixed wrong users count in groups, in case several of them share the same ```sn``` field - see [0d7ab2](https://github.com/georchestra/georchestra/commit/0d7ab2ae1767e13af17d7f7d5f87ad22b728162d), - * ldapadmin: group description editing does not detach users from group anymore - see [#650](https://github.com/georchestra/georchestra/issues/650), - * mapfishapp: restored GetFeatureiInfo queries on aggregated layers - see [#658](https://github.com/georchestra/georchestra/issues/658), - * mapfishapp: truly independant tabs in resultspanel - see [#671](https://github.com/georchestra/georchestra/issues/671), - * mapfishapp: fixed missing drillDown option for getFeatureInfo control - see [#674](https://github.com/georchestra/georchestra/issues/674), - * mapfishapp: multilayer querier tool - several issues fixed, - * mapfishapp: fixed file upload limits values (8Mb limit really enforced) - see [#592](https://github.com/georchestra/georchestra/issues/592), - * mapfishapp: fixed thesaurus keywords request lang - see [#624](https://github.com/georchestra/georchestra/issues/624), - * mapfishapp: compatibility with WMS 1.1.0 servers - see [#663](https://github.com/georchestra/georchestra/issues/663), - * mapfishapp: fixed IE10-specific issue with Ext.data.XmlReader, - * proxy: ignores non HTTPS request check, to allow OGC web service usage, see [a24c74](https://github.com/georchestra/georchestra/commit/a24c7427a484028a5be211c3d6cbe516dbf2c04b), - * proxy: returns earlier in case of 403|404 error code - see [#506](https://github.com/georchestra/georchestra/issues/506), - * proxy: really customizable error pages, - * proxy: always remove incoming sec-* headers, - * proxy: members of the ```ADMINISTRATOR``` group do not need to belong to another ```SV_*``` group anymore - see [ad6784](https://github.com/georchestra/georchestra/commit/ad67845c41af94ee9f2636cc54da7fe88d29c22b), - * proxy: added QuantumGIS to the list of clients triggering basic auth, thus allowing access to protected layers (along with uDig and ArcGIS clients). - - -## INSTALLING: - -Please refer to [this guide](INSTALL.md). - - -### UPGRADING: - -The way geOrchestra is configured has been streamlined: - - there are **default parameters which are shared by several modules**, in [config/shared.maven.filters](config/shared.maven.filters). A "standard" install should not require you to bother about them. But if your setup is different from the default one, you may have to copy one or more of theses properties into your own shared maven filters (read on), in order to be able to customize them. - - there are also **shared parameters which have to be customized** for your own instance. These can be found in the **build_support/shared.maven.filters** file inside your own configuration directory. As your config dir inherits from the template config dir, it should be very similar to [georchestra/template:build_support/shared.maven.filters](https://github.com/georchestra/template/blob/master/build_support/shared.maven.filters). The shared maven filters from this file override those from [config/shared.maven.filters](config/shared.maven.filters) - - finally, there are **parameters for every individual geOrchestra module** (geoserver, geofence, mapfishapp, extractorapp, proxy, ldapadmin), which can be customized via the **build_support/GenerateConfig.groovy** file in your own config dir. Have a look at the one provided by the template config for an example: [georchestra/template:build_support/GenerateConfig.groovy](https://github.com/georchestra/template/blob/master/build_support/GenerateConfig.groovy). - -As a result, a "standard geOrchestra configuration" should not require you to edit more than 2 files: one for the shared parameters, and an other one for module-specific parameters. -Note: copying maven.filter files in your own configuration dir (which was an older practice) is not anymore recommended because it is very difficult to maintain when upgrading version. -=> you should remove any maven.filter file from your own configuration, and eventually copy the values you had customized into build_support/GenerateConfig.groovy - - -**Build:** - -The "Jetty Maven2" Repository provided by oss.sonatype.org requires HTTPS now. The fix was pushed to the georchestra and geonetwork repositories, but in the mean time, your /home/$USER/.m2 local repository might have gotten corrupted with html files rather than jars. If you experience compilation errors, you might need to clean your local maven repo, by running ```rm -rf ~/.m2/repository/org/apache/maven``` - -Upgrading your build is a 2-steps process. -Let's assume your local geOrchestra source code repository is located in ```/path_to_georchestra_root/```. - -First you have to update the sources from the remote origin and update yours: -``` -cd /path_to_georchestra_root/ -git fetch origin -git checkout 14.06 -git submodule sync -git submodule update --init -``` - -Then you need to update your configuration directory to align it with the template configuration, [branch 14.06](https://github.com/georchestra/template/tree/14.06). - -In the following, we assume that your configuration directory is versioned with git, and has the geOrchestra template config set as "upstream" remote repository: -``` -cd /path_to_georchestra_root/config/configuration/ -git fetch upstream -git merge upstream/14.06 -``` -Then you'll probably have to fix some conflicts. -Note: if you do not know how to fix these conflicts, you're probably better off starting again with a fresh fork of the template config directory. - - -The build process remains unchanged, but there is a difference at the end. -In the previous releases, the artifacts included a "-private" suffix in their name. We were making the assumption that all WARs would be deployed to the same tomcat. -This is not the case anymore. This implies that the security proxy now resides in a different servlet container than the proxied webapps. If this is not your case, juste rename them back with the "-private" suffix. - - -**LDAP:** - -The [LDAP repository](https://github.com/georchestra/LDAP) holds branches for the 13.09, 14.01 and 14.06 releases. -In 13.09, groups are instances of posixGroup. From 14.01 to 15.06, they are instances of groupOfNames. -Since 15.12, they are instances of groupOfMembers which allows to have empty groups. - -Between 14.01 and 14.06 branches, here are the differences: - - the ```MOD_EXTRACTORAPP``` group was created. You should assign to it the users which already had access to extractorapp (typically members of ```SV_USER```) - - the privileged geoserver user ("shared.privileged.geoserver.user") was renamed from ```extractorapp_privileged_admin``` to ```geoserver_privileged_user``` because it is no more used only for the extractor. - - -**Databases:** - -In this release, the GeoNetwork database was merged into the unique default georchestra database. -It is now a schema inside the main database. - -This change requires that a "geonetwork" db user is created and granted rights: -``` -createuser -SDRIP geonetwork (the expected password for the default config is "www-data") -psql -d georchestra -c 'CREATE SCHEMA geonetwork;' -psql -d georchestra -c 'GRANT ALL PRIVILEGES ON SCHEMA geonetwork TO "geonetwork";' -``` - -The full migration process is described in [#601/comment](https://github.com/georchestra/georchestra/issues/601#issuecomment-36889319). -This [link](https://github.com/georchestra/georchestra/issues/601#issuecomment-36890670) also provides hints if you prefer to keep your existing 2-databases setup. - - -**Native libs:** - -The GDAL java bindings (gdal.jar) are no more provided by webapps, because this can lead to issues when the native libs are loaded several times in the same servlet container. -Instead, the bindings should be installed once and for all in a folder accessible by the servlet container. - -Please refer to the documentation relative to [native libs handling in geOrchestra](https://github.com/georchestra/georchestra/blob/master/geoserver/NATIVE_LIBS.md), section "Tomcat configuration" - - -**Shared maven filters:** - -Several previous shared maven filters have been removed because they were not truly shared between modules: - * ```shared.mapfishapp.docTempDir``` - * ```shared.geofence.*``` - * ```shared.checkhealth.*``` - -You'll find the exact same settings in build_support/GenerateConfig.groovy - -```shared.geonetwork.dir``` was removed because it was useless - -Seevral shared maven filters were homogeneized: - * shared.psql.ogc.statistics.db becomes shared.ogc.statistics.db - * shared.psql.download_form.db -> shared.download_form.db - * shared.psql.geonetwork.db -> shared.geonetwork.db - * shared.ldapadmin.db - * shared.geofence.db - -Finally, ```geonetwork.language``` was renamed into ```shared.geonetwork.language``` - - -**Miscellaneous:** - -If you're using ldapadmin, make sure you've setup [ReCaptcha](http://www.google.com/recaptcha/) keys for your own domain. -Hint: search for privateKey in your GenerateConfig.groovy. - -French projections (typically EPSG:2154) have been removed from the extractorapp and mapfishapp config files. -Be sure to check your GEOR_custom.js files if you need them. - -mapfishapp: ```GEONETWORK_URL``` was renamed into ```GEONETWORK_BASE_URL```. -Caution: the expected string for this config option has changed too: from eg. "http://geobretagne.fr/geonetwork/srv/fre" to: "http://geobretagne.fr/geonetwork". - - - - -Version 14.01 -============== - -This release was supported by the GeoPicardie, PIGMA, CIGALsace and GeoBretagne projects. -The CRAIG (Centre Régional Auvergnat de l'Information Géographique) is also to be thanked for some nice patches. - -New features: - * [GeoFence](https://github.com/geosolutions-it/geofence/blob/master/README.markdown) integration (not activated by default), see [#534](https://github.com/georchestra/georchestra/issues/534) and the [documentation](https://github.com/georchestra/geofence/blob/georchestra/georchestra.md) - * geoserver: new kml and tiff+tab datastores, see [#557](https://github.com/georchestra/georchestra/issues/557) - * geoserver: now supports data security with GWC requests, see [GEOS-4217](http://jira.codehaus.org/browse/GEOS-4217) and [geoserver/geoserver#341](https://github.com/geoserver/geoserver/pull/341) - * mapfishapp: multi-layer querier tool (thanks to the [CRAIG](http://craig.fr/)) - see [#435](https://github.com/georchestra/georchestra/pull/435) - * mapfishapp: extractor addon - see the [README](mapfishapp/src/main/webapp/app/addons/extractor/README.md) - * mapfishapp: OpenLS addon - see the [README](mapfishapp/src/main/webapp/app/addons/openls/README.md) - * mapfishapp: editor revamped - read [this](mapfishapp/README.md#feature-editor) - * mapfishapp: document persistence in database - see [#443](https://github.com/georchestra/georchestra/issues/443) - -Enhancements: - * analytics: translated to ES, thanks to [GeoBolivia](http://geo.gob.bo/) ! - * analytics: tabs displayed or not based on ```shared.download_form.activated``` and ```shared.ogc.statistics.activated``` values. - * doc: improved [installation instructions for gdal native libs](INSTALL.md#gdal-for-geoserver-extractorapp--mapfishapp) - * doc: installation instructions updated with [GeoServer fine tuning instructions](INSTALL.md#geoserver) - * doc: added a [README](mapfishapp/src/main/webapp/app/addons/README.md) for mapfishapp addons - * js minification: test that jsbuild is working, rather than only testing if venv exists - * ldapadmin: in the mail sent to the moderator, the "from" field is set to the user email - see [#380](https://github.com/georchestra/georchestra/pull/380) - * ldapadmin: improved description fields - see [#400](https://github.com/georchestra/georchestra/pull/400) - * ldapadmin: added ability to configure fields in users list - read the [how-to](ldapadmin/README.md#configure-the-look-of-the-users-list) - * ldapadmin: in case of duplicated email error, the message is more explicit - * ldapadmin: the "lost password" has become a "password recovery" page, which is more neutral, and allows one to define a password even if none pre-exists. - * mapfishapp: WMS DescribeLayer on each WMS layer - see [#401](https://github.com/georchestra/georchestra/pull/401) - * mapfishapp: new layer menu item to set layer as baselayer/overlay - see [#445](https://github.com/georchestra/georchestra/pull/445) - * mapfishapp: preserve the full attribution information on context save/restore - see [#422](https://github.com/georchestra/georchestra/pull/422) - * mapfishapp: cswquerier: better results count - * mapfishapp: wms layer tab: red crosses removed, icons centered - * mapfishapp: backbuffer hidden behind baselayer for non-opaque baselayers - see [#411](https://github.com/georchestra/georchestra/pull/411) - * mapfishapp: allow to POST the url of a WFS service or layer - see [#392](https://github.com/georchestra/georchestra/pull/392) - * mapfishapp: baselayers have a specific color in the layer manager - customizable, see [0a56ed](https://github.com/georchestra/georchestra/commit/0a56edc8e0ea6361e056ce30047d8eddaa7c4c75) - * mapfishapp: contextual help bubbles (eg: on layer edit activated), see [#466](https://github.com/georchestra/georchestra/issues/466) - * mapfishapp: print now supports WMS 1.3.0-only capable servers, see [#511](https://github.com/georchestra/georchestra/issues/511) - * mapfishapp: annotation addon: added an icon & made the window closable - * mapfishapp: OGC Exception Report handling deactivated during context restore - see [#532](https://github.com/georchestra/georchestra/issues/532) - * mapfishapp: allow to show login/logout button in toolbar even if header is shown - see [#43](https://github.com/georchestra/georchestra/issues/43) - * mapfishapp: file upload now reports more accurately errors - see [#402](https://github.com/georchestra/georchestra/issues/402) - * mapfishapp: file upload: a spinner is shown while a file is uploaded - * mapfishapp: contexts can now store title + abstract fields - see [#443](https://github.com/georchestra/georchestra/issues/443) - * mapfishapp: added ability to send contexts or maps to any external application - see [#443](https://github.com/georchestra/georchestra/issues/443) - * ogc-server-statistics: now logging WMTS GetTile, WMS GetStyles + WFS2 operations, see [#527](https://github.com/georchestra/georchestra/issues/527) - * proxy: new filter to make basic auth challenge if https and matches user-agent, useful for ArcGIS clients - read the [notes](https://github.com/georchestra/georchestra/commit/8828a11ffb0cb716ad0a6bb1f847ce24328ea450) - * proxy: overridable HTTP 40x error pages, see for instance [config/defaults/security-proxy/403.jsp](config/defaults/security-proxy/403.jsp) - * proxy: does not send sec-username & sec-roles headers when the user is anonymous, see [#223](https://github.com/georchestra/georchestra/pull/223) - * static: module is now called "header" - * downloadform, ogcstatistics and ldapadmin modules now share the same postgresql database by default, each in their own schema. Please refer to [#516](https://github.com/georchestra/georchestra/issues/516) and the migration guide below. - -Bug fixes: - * analytics: fixed wrong password for jdbc postgresql - * extractorapp: fixed the ```checkPermission``` method for local layers - * extractorapp: fixed impossibility to switch to french when default lang is english or spanish - * extractorapp: fixed invalid buffer combo text - * extractorapp: removed useless classes - see [#551](https://github.com/georchestra/georchestra/issues/551) - * extractorapp: bbox writer always uses geotools' ShpFeatureWriter, which allows extractorapp to not rely mandatorily on gdal/ogr native libs - see [#409](https://github.com/georchestra/georchestra/issues/409) - * extractorapp: fixed parameter order on CheckFormAcceptance bean instantiation - see [b299ec](https://github.com/georchestra/georchestra/commit/b299ec9f55777ef9f3610c14f01e0449e0067f3c) - * extractorapp: fixed wrong jsessionid used to check ability to download resource - see [#558](https://github.com/georchestra/georchestra/issues/558) - * extractorapp: fixed CheckFormAcceptance SQL test - * geonetwork: download form now opens also in metadata view if activated, see [#416](https://github.com/georchestra/georchestra/issues/416) - * geonetwork: fixed missing thumbnail in CSW query requesting DC in full mode for profil France records - * geonetwork: thumbnails: add protocol for JPG (csw) - * geonetwork: widgets / keyword selection / support 2 concepts with same label - * geonetwork: editor / XML view / do not escape - * geonetwork: ISO19110 / fixed missing label. Add the capability to set contact logo. - * geonetwork: spatial index / fixed corrupted shapefile when empty polygon. - * geonetwork: ISO19110 / relation now displays title. - * geonetwork: properly unzip file - * geonetwork: widgets / properly propagate sortby options. - * geonetwork: fixed map coords position when page scrolled - * geonetwork: fixed facet layout issue - * geonetwork: widgets / add privileges panel to batch operation. - * geonetwork: ISO19139 / improve labels - * geonetwork: fixed tooltip display error on IE - * geonetwork: hide user menu if hideSignOut option is enable IE - * geonetwork: add option to hide sign out action from user menu. - * geonetwork: editor / suggestion / save changes before processing - * geonetwork: RSS / add URL parameter to only return one link for each metadata - * geonetwork: widgets / action menu is now in a custom element in the template - * geonetwork: put default list width of some other search criterias to auto - * geonetwork: search suggestion / properly returned field value with line break - * geoserver: fixed "inspire extension not deployed" - * header: fixed IE8 compatibility + header frameborder size set to 0 - * header: the platform-wide language set by ```shared.language``` is now enforced in the header module, see [#540](https://github.com/georchestra/georchestra/issues/540) - * ldapadmin: fixed incorrect generation of login - see [#344](https://github.com/georchestra/georchestra/pull/344) - * ldapadmin: remove user from groups after deleting the user - see [#406](https://github.com/georchestra/georchestra/pull/406) - * ldapadmin: prevent race condition when opening ```/#groups/:group``` directly. - * ldapadmin: fixed erroneous admin count - see [#405](https://github.com/georchestra/georchestra/pull/405) - * ldapadmin: send HTTP 403 status code when access is forbidden, not 200 - see [#244](https://github.com/georchestra/georchestra/issues/244) - * ldapadmin: normalizes the user input so that we consider the uid is always lowercased - see [#565](https://github.com/georchestra/georchestra/issues/565) - * ldapadmin: fixed missing header on the account/newPassword page - * ldapadmin: fixed group ordering in privateui - * mapfishapp: fixed incorrect styles ordering - * mapfishapp: annotation addon: fixed label size - * mapfishapp: fix for vector features incompletely displayed - see [#367](https://github.com/georchestra/georchestra/pull/367) - * mapfishapp: buggy legend url does not make the print fail anymore- see [#362](https://github.com/georchestra/georchestra/pull/362) - * mapfishapp: ```window.onbeforeunload``` should not return null (fixed annoying IE popup) - * mapfishapp: fixed "too many features" message in referentials search - * mapfishapp: fixed WMS GetFeatureInfo feature reprojection in IE - * mapfishapp: always compute data model on getfeatureinfo - * mapfishapp: always prefer ```text/html``` metadataURL entries - * mapfishapp: fixed WFS2 capabilities - see [#373](https://github.com/georchestra/georchestra/pull/373) - * mapfishapp: fixed JPEG layers in WMC loaded as PNG - see [#370](https://github.com/georchestra/georchestra/pull/370) - * mapfishapp: connection link in toolbar when header height is set to 0 - * mapfishapp: referential recenter : fix for old fashioned namespace usage - * mapfishapp: fixed missing dependency to WFSCapabilitiesReader - * mapfishapp: annotation addon: fixed calling ```createUrlObject``` with an object parameter - see [#437](https://github.com/georchestra/georchestra/pull/437) - * mapfishapp: in layerfinder, fixed incorrect reference to ```OpenLayers.i18n``` method - * mapfishapp: file upload - the limit is 8MB, not 8GB - * mapfishapp: file upload - better handling of server-side errors - * mapfishapp: fixed extra comma (IE) in GEOR_print - * mapfishapp: fixed wrong popup anchor position on edit - see [#456](https://github.com/georchestra/georchestra/pull/456) - * mapfishapp: annotations: fixed popup anchor - see [#366](https://github.com/georchestra/georchestra/pull/366) - * mapfishapp: fixed potentially incorrect metadata url - see [#454](https://github.com/georchestra/georchestra/pull/454) - * mapfishapp: less strict filtering of suitable images for thumbnail display in CSW querier - * mapfishapp: fixed missing dependency to WKT format - see [#482](https://github.com/georchestra/georchestra/pull/482) - * mapfishapp: fixed incorrect ```maxScaleDenominator``` on WMC restored - see [#431](https://github.com/georchestra/georchestra/pull/431) - * mapfishapp: attribution logo correctly sized - see [#490](https://github.com/georchestra/georchestra/pull/490) - * mapfishapp: fixed wrong proxy selected when the webapp name does not contain private - see [#509](https://github.com/georchestra/georchestra/pull/509) - * mapfishapp: fixed various problems in legend printing - * mapfishapp: fixed missing ```GEOR.config.USEREMAIL``` (used by the extractor addon) - * mapfishapp: set a white background to the overview map in the printed PDF - see [#372](https://github.com/georchestra/georchestra/pull/372) - * mapfishapp: overview map now working at most small scales - see [#513](https://github.com/georchestra/georchestra/issues/513) - * mapfishapp: fixed magnifier tool - see [#500](https://github.com/georchestra/georchestra/issues/500) - * mapfishapp: fixed too much space between elements in FireFox - see [#539](https://github.com/georchestra/georchestra/issues/539) - * mapfishapp: print: white background for the overview map, see [b09cc9](https://github.com/georchestra/template/commit/b09cc94dcb66186b2ca48d5d0df5b2b7b95e1ed8) - * mapfishapp: print: scaled down legend icons to match map icons size, see [436913](https://github.com/georchestra/template/commit/43691352bc81d024dff01245ba33c47605c7a607) - * mapfishapp: print: limit legend texts width, and wrap them, see [78c05d](https://github.com/georchestra/template/commit/78c05d9d01699411df282ae6fca1965a9825b21b) - * mapfishapp: print: left align the legend to its column container, see [d707a8](https://github.com/georchestra/template/commit/d707a8f7371bf56059758802e7afbb891f34bfce) - * mapfishapp: fixed incorrect metadata URL in csw browser ("add a layer from thesaurus") - see [#542](https://github.com/georchestra/georchestra/issues/542) - * ogcservstatistics - fixed missing postgresql driver loading - * proxy: fixed charset detection in ArcGIS server responses - see [#498](https://github.com/georchestra/georchestra/pull/498) - * proxy: removed ```sec-*``` headers from client request - see [#154](https://github.com/georchestra/georchestra/pull/154) - * proxy: fixed incorrect referer value - see [#533](https://github.com/georchestra/georchestra/issues/533) - * header: maintains existing URI parameters when adding the "login" param - see [#175](https://github.com/georchestra/georchestra/issues/175) - * build now passes on windows. - -### UPGRADING: - * As a result of [#569](https://github.com/georchestra/georchestra/issues/569), LDAP groups are now ```groupOfNames``` instances rather than ```posixGroup``` instances. You have to migrate your LDAP tree, according to the following procedure (please change the ```dc=georchestra,dc=org``` string for your own base DN): - * dump your ldap **groups** with: - - ``` - ldapsearch -H ldap://localhost:389 -xLLL -D "cn=admin,dc=georchestra,dc=org" -w your_ldap_password -b "ou=groups,dc=georchestra,dc=org" > /tmp/groups.ldif - ``` - - * migration: - - ``` - sed -i 's/\(memberUid: \)\(.*\)/member: uid=\2,ou=users,dc=georchestra,dc=org/' /tmp/groups.ldif - sed -i 's/posixGroup/groupOfNames/' /tmp/groups.ldif - sed -i '/gidNumber/d' /tmp/groups.ldif OR sed -i 's/gidNumber/ou/' /tmp/groups.ldif if geofence is deployed - sed -i 's/objectClass: groupOfNames/objectClass: groupOfNames\nmember: uid=fakeuser/' /tmp/groups.ldif - ``` - - * drop your groups organizationalUnit (```ou```) - * optionally, have a look at the provided [georchestra-memberof.ldif](https://github.com/georchestra/LDAP/blob/master/georchestra-memberof.ldif) file, which creates & configures the [memberOf overlay](http://www.openldap.org/doc/admin24/overlays.html). As root, and after checking that the file targets the correct database (```olcDatabase={1}hdb``` by default): ```ldapadd -Y EXTERNAL -H ldapi:// < georchestra-memberof.ldif``` - * import the updated groups.ldif file. - * analytics: the ExtJS submodule path has changed, be sure to run ```git submodule update --init``` when you switch branches. - * databases: the downloadform, ogcstatistics and ldapadmin databases are now merged into a single one named "georchestra". Each webapp expects to find its tables in a dedicated schema ("downloadform" for the downloadform module, "ogcstatistics" for ogc-server-statistics, and "ldapadmin" for ldapadmin). See [#535](https://github.com/georchestra/georchestra/pull/535) for the complete patch. If you currently have one dedicated database for each module, you can keep your setup, provided you customize the ```shared.psql.ogc.statistics.db```, ```shared.psql.download_form.db``` & ```shared.ldapadmin.db``` maven filters in your own config. In any case, you'll have to rename the ```download``` schema (of the previous ```downloadform``` database) into ```downloadform```, and migrate the tables which were in the public schema of the databases ```ogcstatistics``` and ```ldapadmin``` into the newly created schemas. - -Example migration script: - -``` -psql -d downloadform -c 'alter schema download rename to downloadform;' - -wget --no-check-certificate https://raw.githubusercontent.com/georchestra/georchestra/14.01/ldapadmin/database.sql -O /tmp/ldapadmin.sql -psql -d ldapadmin -f /tmp/ldapadmin.sql -psql -d ldapadmin -c 'GRANT ALL PRIVILEGES ON SCHEMA ldapadmin TO "www-data";' -psql -d ldapadmin -c 'GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA ldapadmin TO "www-data";' -psql -d ldapadmin -c 'GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA ldapadmin TO "www-data";' -psql -d ldapadmin -c 'insert into ldapadmin.user_token (uid, token, creation_date) select uid, token, creation_date from public.user_token;' -psql -d ldapadmin -c 'drop table public.user_token;' - -wget --no-check-certificate https://raw.githubusercontent.com/georchestra/georchestra/14.01/ogc-server-statistics/database.sql -O /tmp/ogcstatistics.sql -psql -d ogcstatistics -f /tmp/ogcstatistics.sql -psql -d ogcstatistics -c 'GRANT ALL PRIVILEGES ON SCHEMA ogcstatistics TO "www-data";' -psql -d ogcstatistics -c 'GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA ogcstatistics TO "www-data";' -psql -d ogcstatistics -c 'GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA ogcstatistics TO "www-data";' -psql -d ogcstatistics -c 'insert into ogcstatistics.ogc_services_log (id, user_name, date, service, layer, request, org) select id, user_name, date, service, layer, request, org from public.ogc_services_log;' -psql -d ogcstatistics -c 'drop table public.ogc_services_log;' -``` - - * download form: the module is disabled by default (```shared.download_form.activated=false```). Be sure to set the value you want in your shared.maven.filters file. - * extractorapp: - * ```BUFFER_VALUES``` has changed. If you had a custom value in your GEOR_custom.js file, you have to modify it according to the new syntax. - * the ```geobretagne_production``` env variable has been removed - see [#97](https://github.com/georchestra/georchestra/pull/97) - * geoserver: be sure to set the ```file.encoding``` tomcat option for geoserver to interpret correctly UTF-8 SLDs (read [how](INSTALL.md#geoserver)). - * ldapadmin: - * accessing ```/ldapadmin/privateui/``` is now restricted to members of the ```MOD_LDAPADMIN``` group. It is recommended that only members of the ```ADMINISTRATOR``` or ```SV_ADMIN``` administrative groups belong to ```MOD_LDAPADMIN```, since this group allows privileges escalation. - * new ```shared.ldapadmin.db``` parameter to specify the ldapadmin database name (defaults to "georchestra"). - * the ldapadmin private app is now accessed via /ldapadmin/privateui/ rather than /ldapadmin/privateui/index.html - * mapfishapp: - * geonames now require you to create an account in order to enable queries on their free web services (see [#563](https://github.com/georchestra/georchestra/issues/563)). Please change the default account in your profile's GEOR_custom.js ```GEONAMES_FILTERS``` variable. - * addons: custom addons relying on local web services should no longer assume that the application path is ```/mapfishapp```. Instead, they should use the new ```GEOR.config.PATHNAME``` constant, eg [here](https://github.com/georchestra/georchestra/blob/04017309f3880a0c558537235c92f70a269722d1/mapfishapp/src/main/webapp/app/addons/annotation/js/Annotation.js#L486). - * the app now requires a dedicated database schema, please refer to the [INSTALL.md](INSTALL.md#postgresql) documentation. - * new config option: ```SEND_MAP_TO``` for [#443](https://github.com/georchestra/georchestra/issues/443), please read the [doc](https://github.com/georchestra/template/blob/34496d62701e809c80235275a9e2a0b4b46f1123/mapfishapp/app/js/GEOR_custom.js#L583). - * new config option: ```FORCE_LOGIN_IN_TOOLBAR``` - * the ```NS_EDIT``` config option has been removed, and mapfishapp/edit is no longer routed. By default, all layers served by the platform geoserver are editable (see ```GEOR.custom.EDITABLE_LAYERS```), provided the user has the rights to (defaults to members of ```ROLE_ADMINISTRATOR```, see ```GEOR.custom.ROLES_FOR_EDIT```). - * the contexts referenced in your ```GEOR.custom.CONTEXTS``` array are now able to reference layers with their full attribution information (text, logo & link). Have a look at the provided [default.wmc](https://github.com/georchestra/template/blob/55f24c8625e737d0b4567db92966c98502578766/mapfishapp/default.wmc#L39). - * print: some parameters have changed when the print module was updated: ```maxIconWidth``` -> ```iconMaxWidth```, ```maxIconHeight``` -> ```iconMaxHeight``` (see [e6231c](https://github.com/georchestra/template/commit/e6231c8cbf325dfa2bf96fcaa14096fc0c64ab89)). - * ogcservstatistics - disabled by default: ```shared.ogc.statistics.activated=false```. Be sure to set the value you want in your shared.maven.filters file. - * static: the "static" module has been renamed into "header": your deployment scripts *must* be adapted, as well as your apache2 configuration (or any other reverse proxy). - - -Version 13.09 -============== - -This major release was supported by the GeoPicardie, PIGMA, CIGALsace and GeoBretagne projects. -The CRAIG (Centre Régional Auvergnat de l'Information Géographique) is also to be thanked for some nice patches. - -New features: - * mapfishapp: [annotation addon](https://github.com/georchestra/georchestra/tree/master/mapfishapp/src/main/webapp/app/addons/annotation) with KML export, - * mapfishapp: geofile upload: support of SHP, MIF/MID, GML, KML by default (uses a geotools implementation). Support of TAB and GPX when OGR is available (read this [how to](https://github.com/georchestra/georchestra/tree/master/mapfishapp#optional-install-gdal-native-library)), - * mapfishapp: WMS + WFS version autodetection, - * mapfishapp: WMTS 1.0.0 support, - * mapfishapp: WFS 2.0.0 support, - * mapfishapp: WMS 1.3.0 support, - * mapfishapp: OWSContext 0.3.x READ support (WMS layers), - * mapfishapp: French IGN's GeoPortail webservices support, - * mapfishapp: Russian translation (!), - * geoserver: Opaque WMS Layer property support (see [#158](https://github.com/georchestra/georchestra/issues/158)), - * ldapadmin: a brand new module is available which allows at the same time to: admin your users in your browser, let your users recover their lost password, and let them register too ! More information in the module [README](ldapadmin/README.md), - * extractorapp: ship metadata in ZIP (read this [how to](extractorapp/README.md#metadata-extraction)), - * extractorapp: now supports KML & TIF+(TFW,TAB) output. - -Enhancements: - * mapfishapp: external libs such as ExtJS updated to 3.4.1.1 (for IE 10 support), GeoExt and OpenLayers updated to master (yeah !). This brings greater navigation ease and support for other cool stuff. - * mapfishapp: added ability to restore contexts with a projection different from the map's (assuming layers will be able to reproject), - * mapfishapp: print improved (updated to MapFish Print 2.0 for WMTS and GeoPortail support, brand new templates, natural 91 dpi resolution and new "comment" field), - * mapfishapp: referentials search is no more case sensitive with WFS2, - * mapfishapp: improved syntax for metadata search (via CSW), see [#325](https://github.com/georchestra/georchestra/pull/325), - * mapfishapp: true print extent displayed, - * mapfishapp: querier radius is now dynamically displayed in meters/km, - * mapfishapp: WFS layers feature selection & attributes viewing, - * mapfishapp: layer name and source smartly ellipsed with CSS rather than JS, - * mapfishapp: do not close window on style applied, - * mapfishapp: layer style list is now alphabetically sorted, - * mapfishapp: permalink validity displayed in months, - * mapfishapp: link to layer metadata which is declared as text/html is now prefered over the other links, - * mapfishapp: addons can now be loaded by default (read [how](https://github.com/georchestra/template/blob/45eddec545418b4de55952795c66940729d3b547/mapfishapp/app/js/GEOR_custom.js#L64)), - * mapfishapp: added a [note on data](mapfishapp/README.md#your-data-in-mapfishapp) in the doc, - * extractorapp: several small fixes for extractorapp reprojection, - * extractorapp: more visible extract button, - * extractorapp: auto-deactivate the "Modify the bbox" button, - * geonetwork: validation report: translated all reported XSD errors in editor, - * geonetwork: user menu: improved links according to role, - * geonetwork: RSS: added URL parameter to only return one link for each metadata, - * geonetwork: widgets: added privileges panel to batch operation, - * geonetwork: improved icons plus a custom icon for XLS files, - * SDI Instance name in page titles across the SDI (```shared.instance.name```, defaulting to "geOrchestra"), - * everywhere: plain text emails, with the ability to switch back to HTML via ```shared.email.html```, - * everywhere: all outgoing emails are now prefixed with the platform name, - * everywhere: better translations. - * documentation: improved [installation](INSTALL.md) instructions - -Bug fixes: - * security-proxy: now only sends one referer headers - fixes consuming arcgis web services - [read more](https://github.com/georchestra/georchestra/issues/266), - * geoserver: fixed incorrect WMS 1.3.0 scaledenominator values, see [#264](https://github.com/georchestra/georchestra/issues/264), - * geonetwork: editor: suggestion: changes saved before processing, - * geonetwork: user menu: IE compatibility issues fixed, - * static: fixed incorrect login link on CAS pages, - * cas: fixed IE8 JS error on login page, - * extractorapp: fixed app loading on IE8, - * extractorapp: now reports broken or uninstalled GDAL libraries, - * mapfishapp: WM[T]S GetFeatureInfo geometries on the fly reprojection (at last !) - as a result, it is advised to fill the most widely used SRSes in your country in your [GEOR_custom.js](https://github.com/georchestra/template/blob/45eddec545418b4de55952795c66940729d3b547/mapfishapp/app/js/GEOR_custom.js#L365) config file, - * mapfishapp: fixed fontFamily not taken into account by styler - also requires ```apt-get install ttf-mscorefonts-installer```, - * mapfishapp: fixed querier setup issue when WFS service is not available, - * mapfishapp: more robust layer hydrating from namespaced capabilities, - * mapfishapp: fixed zooming occuring while drawing features, - * mapfishapp: mouse position rounding is now correct for ETRS89, RGF93 and other long-lat based projections, - * mapfishapp: fixed scrolling on a map without any visible layer, - * mapfishapp: fixed unresolved images for point symbolizer combo, - * mapfishapp: fixed legend label not appearing when only one class is available, - * mapfishapp: fixed incorrect describeFeatureType URLs, - * mapfishapp: fixed undefined addons title and description when lang dict does not exist yet, - * mapfishapp: fixed broken referentials search in IE8, - * mapfishapp: fixed broken help url - - -### UPGRADING: - * mapfishapp: - * default projection changes from EPSG:2154 to EPSG:3857 (aka Spherical Web Mercator). Your users might need to clear their localStorage, or force loading of the new default context. - * default MAP_SCALES changes to match the OGC WMTS spec, - * LDAP: see [georchestra/LDAP#2](https://github.com/georchestra/LDAP/pull/2) - * one group was renamed: ```STAT_USER``` becomes ```MOD_ANALYTICS``` - grants access to the analytics app, - * an other one was created: ```MOD_LDAPADMIN``` - grants access to the LDAPadmin private UI (/ldapadmin/privateui/). - * The default application language is now **English**: - * ```shared.language``` = en - * ```geonetwork.language``` = eng - * default email templates [here](https://github.com/georchestra/georchestra/tree/master/config/defaults/ldapadmin/WEB-INF/templates) and [there](https://github.com/georchestra/georchestra/tree/master/config/defaults/extractorapp/WEB-INF/templates): be sure to override them in your own config ! - * Remember also to fill these new global maven filters: - * ```shared.homepage.url``` - for your SDI home page (might be something like http://my.sdi.org/portal/), - * ```shared.instance.name``` - will be displayed in page titles (eg: GeoMyCompany), - * ```shared.email.html``` - whether to send emails in plain text (default) or HTML, - * ```shared.administrator.email``` - this email receives new account requests (eg: me@mycompany.com) - * shared maven filters renamed: - * ```shared.smtp.replyTo``` -> ```shared.email.replyTo``` - * ```shared.smtp.from``` -> ```shared.email.from``` - * frontend webserver: - * add a proxy rule for `/_static/` subdirectory (see https://github.com/georchestra/georchestra/tree/master/INSTALL.md) - - -Version 13.06 -============== - -This release was supported by the French GeoPicardie, GeoBretagne and CIGALsace projects, the GIP ATGeRi and individual contributors. - -New features: - * geoserver: updated to 2.3.2, - * geoserver: patched to include ScaleHint (WMS 1.1.x) and Min/MaxScaleDenominator (WMS 1.3.0) in the capabilities documents, according to the default layer SLD. The patched was accepted in GeoServer master, see http://jira.codehaus.org/browse/GEOS-572 - -Enhancements: - * the header is now part of the static module, and all webapps make use of it. As a result, requests to static pass through the security-proxy, - * the header height can be set across all applications by the way of a unique shared maven filter, - * mapfishapp: the CSW querier filters results on type = dataset OR series, - * extractorapp: supported output formats combos made configurable, ECW output format expunged by default, - * the template config was slimmed down: files where default values are suitable for most deployments were moved to config/defaults, - * the documentation was improved ([mapfishapp](mapfishapp/README.md#feature-editor), [install](INSTALL.md), [manage configs](README.md#how-to-customize-)), - * we now have CI on JS files syntax with Travis ! - -Bug fixes: - * fixed the ability to turn off the downloadform with shared.downloadform.activated, - * mapfishapp: referentials search now sends the map srs in the WFS query to allow feature reprojection, - * mapfishapp: fixed incorrect thumbnail URLs in CSW querier, - * mapfishapp: fixed misaligned thumbnail in CSW querier, - * mapfishapp: fixed window + grid problems on service opening, see [issue 109](https://github.com/georchestra/georchestra/issues/109), - * mapfishapp: fixed error in GEOR.ows.hydrateLayerRecord when falling back to main service, - * mapfishapp: fixed links to MD sheet, see [issue 110](https://github.com/georchestra/georchestra/issues/110), - * mapfishapp: fixed broken legend after styling, see [issue 107](https://github.com/georchestra/georchestra/issues/107), - * mapfishapp: more robust handling of incoming WMS server URLs (eg: those with a mapfile GET parameter), - * geonetwork: fixed ldap attribute mapping. - -### UPGRADING: - * mapfishapp's default thesaurus has been set to local.theme.test, which is the only one exported by GeoNetwork by default. Feel free to customize to suit your needs, - * geonetwork upgrade instructions are available [here](https://github.com/georchestra/geonetwork/blob/georchestra-29/README.md). - - -Version 13.02 -============= - -This release was made possible thanks to support from the French GIP ATGeRi (http://cartogip.fr/) and contributors. - -New features: - * geoserver: updated to 2.3.0, see http://blog.geoserver.org/2013/03/18/geoserver-2-3-0-released-first-official-osgeo-release/ - * geoserver: useful extensions added in template profile, see http://applis-bretagne.fr/redmine/issues/4217 - * geonetwork: upgraded geonetwork to geonetwork master (2.9.0-pre) - * extractorapp: extraction bbox is now part of the data bundle, see https://github.com/georchestra/georchestra/pull/35 - * mapfishapp: lon, lat and radius GET parameters for startup recentering, see https://github.com/georchestra/georchestra/pull/20 - * mapfishapp: switchable pointer coordinates SRS, see https://github.com/georchestra/georchestra/pull/25 - * mapfishapp: layers drag'n drop in layer manager, see http://applis-bretagne.fr/redmine/issues/1959 - * mapfishapp: OGC context switcher, see https://github.com/georchestra/georchestra/pull/26 - * mapfishapp: print layouts ACL, see https://github.com/georchestra/georchestra/pull/30 - * mapfishapp: spatial query based on a circle, see http://applis-bretagne.fr/redmine/issues/1957 - * mapfishapp: support for addons & magnifier addon, see https://github.com/georchestra/georchestra/pull/36 - * mapfishapp: cadastre addon, see https://github.com/georchestra/georchestra/pull/48 - * mapfishapp: support transitionEffect resize (aka "back buffers") on layers coming from a WMC, see https://github.com/georchestra/georchestra/pull/42 - -Enhancements: - * mapfishapp: results panel displays URLs as html links, see https://github.com/georchestra/georchestra/pull/21 - * mapfishapp: add layer from thesaurus: metadata title first, see https://github.com/georchestra/georchestra/pull/23 - * mapfishapp: more visible layer names, see https://github.com/georchestra/georchestra/pull/22 - * mapfishapp: add zoomout button in the toolbar, see https://github.com/georchestra/georchestra/pull/24 - * mapfishapp: added ability to print protected geoserver layers, see https://github.com/georchestra/template/commit/bb424bd74f7504af93b5e5c708f807ce0b6fdca4 - * mapfishapp: more robust detection of WMS layers in CSW getRecords responses, see https://github.com/georchestra/georchestra/pull/4 - * mapfishapp: window buttons consistency and default actions, see https://github.com/georchestra/georchestra/pull/33 - * mapfishapp: by default, the map is now restored with its latest known state (context), see https://github.com/georchestra/georchestra/pull/50 - * mapfishapp: missing translations - * mapfishapp, downloadform, extractorapp, security-proxy, ogc-server-statistics: the java packages now belong to org.georchestra - * mapfishapp: DocController's maxDocAgeInMinutes was change to manage long integer value, see https://github.com/georchestra/georchestra/pull/81 - -Bug fixes: - * security-proxy: Location header was erroneously removed in some cases, see https://github.com/georchestra/georchestra/commit/fef3d77ab4fe0e6045c47add1f84dbd7de3a8c4e - * mapfishapp: fixed erroneous WMSC2WMS mapping, which prevented printing of the GeoBretagne OSM baselayer, see https://github.com/georchestra/georchestra/commit/159bd4f24ecb21b9c76f76d27c1736ec1040f0ab - * mapfishapp: use toponymName instead of name in GeoNames results, see https://github.com/georchestra/georchestra/issues/45 - * mapfishapp: WFS layer source server now correctly displayed, see https://github.com/georchestra/georchestra/commit/945349a1935286af2e02bfd21f9d7d9eeb6481e7 - * mapfishapp: Styler 2nd load timing out fixed, see https://github.com/georchestra/georchestra/commit/7b28656a2a81d01c00ebe0ff5a55e571f43aa63c - * mapfishapp: download style styler link did not always provide the current layer style, see https://github.com/georchestra/georchestra/commit/5c47caa38b8c975982776f2a35c0574217bc2a17 - * mapfishapp: fixed XML documents missing the prolog, see http://applis-bretagne.fr/redmine/issues/4536 - * mapfishapp: WFS layer redraw was throwing an error, see http://applis-bretagne.fr/redmine/issues/4544 - * LDAP: group membership is now declared with memberUid = user uid rather than full dn, see https://github.com/georchestra/georchestra/pull/91 - -### UPGRADING: - * LDAP tree needs to be migrated as a result of https://github.com/georchestra/georchestra/pull/91 : - * ldif export: ldapsearch -xLLL -D "cn=admin,dc=georchestra,dc=org" -W > dump.ldif - * migration: sed 's/\(memberUid: uid=\)\(.*\)\(,ou=users,dc=georchestra,dc=org\)/memberUid: \2/' dump.ldif > new.ldif - * ldif import - * mapfishapp config changes: - * the print config folder should be moved from YOUR_CONFIG/mapfishapp/print to YOUR_CONFIG/mapfishapp/WEB-INF/print for security reasons, see https://github.com/georchestra/georchestra/issues/82 - * client side (see GEOR_config.js or GEOR_custom.js for more information): - * MAP_POS_SRS1 and MAP_POS_SRS2 options have been replaced with POINTER_POSITION_SRS_LIST - * DEFAULT_WMC option has been replaced with CONTEXTS - * PRINT_LAYOUTS_ACL allows to fine-tune available printing layouts based on user roles - * DEFAULT_PRINT_FORMAT is now replaced by DEFAULT_PRINT_LAYOUT - * DEACCENTUATE_REFERENTIALS_QUERYSTRING option added (controls whether to deaccentuate the referentials widget query string or not) - * server side: - * There is a new maven filter for mapfishapp temporary documents: shared.mapfishapp.docTempDir (defaults to /tmp/mapfishapp) - * don't forget to edit your WMCs to activate back buffers on base layers, see https://github.com/georchestra/georchestra/pull/42 - * In GeoNetwork, it is now recommended to use OGC:WMS protocol rather than OGC:WMS-1.1.1-http-get-map (or any other WMS tagged with a version) to declare WMS layers, see https://github.com/georchestra/georchestra/pull/4 diff --git a/RELEASE_PROCEDURE.md b/RELEASE_PROCEDURE.md index 4a8dc80787..5959539a50 100644 --- a/RELEASE_PROCEDURE.md +++ b/RELEASE_PROCEDURE.md @@ -2,167 +2,186 @@ This is an attempt to write down the procedure to make a release of the geOrchestra SDI. -The use case we describe here is the release of geOrchestra 13.12. +## Major releases -## Configuration template - -Go to the master branch in your template config repository: +### Datadir +From [master](https://github.com/georchestra/datadir/tree/master), create a new branch: ``` -cd georchestra/template git checkout master git pull origin master +git checkout -b 20.1 +git push origin 20.1 ``` -Let's create a new branch for the release: - +Same has to be done for the `docker-master` branch: ``` -git checkout -b 13.12 -git push origin 13.12 +git checkout docker-master +git pull origin docker-master +git checkout -b docker-20.1 +git push origin docker-20.1 ``` -There's nothing more to do here ! - -## LDAP - -Same as above ! -We have to create a dedicated branch for the new release. - -## GeoNetwork - -Go to the georchestra-13.12 branch in your geonetwork repository: +### Docker +From [master](https://github.com/georchestra/docker/tree/master), create a new branch: ``` -cd georchestra/geonetwork -git checkout georchestra-13.12 -git pull origin georchestra-13.12 +git checkout master +git pull origin master +git checkout -b 20.1 ``` -Batch change the version to the release version in every pom.xml: - +Update the image tags: ``` -find ./ -name pom.xml -exec sed -i 's/13.12-SNAPSHOT/13.12/' {} \; +sed -i 's/latest/20.1.x/g' docker-compose.yml ``` -Commit and propagate the changes: - +Make sure the config folder tracks the `docker-20.1` datadir branch: ``` -git commit -am "13.12 release" -git push origin georchestra-13.12 +$ cat .gitmodules +[submodule "config"] + path = config + url = https://github.com/georchestra/datadir.git + branch = docker-20.1 ``` -Now is the time to create the development branch for the future version (14.06): +Manually update the README and `.github/dependabot.yml` ``` -git checkout -b georchestra-14.06 +git commit -am "20.1 branch" +git push origin 20.1 ``` -In the ```georchestra-14.06``` branch, let's create a brand new ```db-migrate-default.sql``` migration file: +### GeoServer minimal datadir +From [master](https://github.com/georchestra/geoserver_minimal_datadir/tree/master), create a new branch: ``` -$ cat > web/src/main/webapp/WEB-INF/classes/setup/sql-georchestra/migrate/1406/db-migrate-default.sql << EOF -BEGIN; -UPDATE Settings SET value='14.06' WHERE name='version'; -UPDATE Settings SET value='0' WHERE name='subVersion'; -COMMIT; -EOF +git checkout master +git pull origin master +git checkout -b 20.1 +git push origin 20.1 ``` -Let's also update the pom's versions: - +Same has to be done for the `geofence` branch: ``` -find ./ -name pom.xml -exec sed -i 's/13.12/14.06-SNAPSHOT/' {} \; +git checkout geofence +git pull origin geofence +git checkout -b 20.1-geofence +git push origin 20.1-geofence ``` -At this point, we should commit and propagate the branch: +### GeoNetwork ``` -git add web/src/main/webapp/WEB-INF/classes/setup/sql-georchestra/migrate/1406/db-migrate-default.sql -git commit -am "Branch georchestra-14.06 created" -git push origin georchestra-14.06 +cd geonetwork +git checkout georchestra-gn3.10.2 +git pull origin georchestra-gn3.10.2 +git tag 20.1.0 +git push origin 20.1.0 ``` -We're done for GeoNetwork ! +Then [create a new release](https://github.com/georchestra/geonetwork/releases). +### geOrchestra -## geOrchestra - -Go to the master branch in your georchestra repository: - +From the master branch of the [georchestra](https://github.com/georchestra/georchestra/tree/master) repository, derive a `20.1.x` branch: ``` -cd georchestra/georchestra git checkout master git pull origin master -git submodule update --init -``` - -Batch change the version to the release version in every pom.xml: - -``` -find ./ -name pom.xml -exec sed -i 's/13.12-SNAPSHOT/13.12/' {} \; -``` - -Reset changes in every submodule: - -``` -git submodule foreach 'git reset --hard' +git checkout -b 20.1.x ``` Update the GeoNetwork submodule to the release commit: - ``` cd geonetwork -git checkout georchestra-13.12 -git pull origin georchestra-13.12 +git fetch origin +git checkout 20.1.0 cd - ``` -Manually update the files mentionning the current release version (```README.md``` and ```RELEASE_NOTES.md```). +Other tasks: + * Manually update the files mentionning the current release version (```README.md``` and ```RELEASE_NOTES.md```) + * Update the branch name for the Travis status logo + * Change the `packageDatadirScmVersion` parameter in the root `pom.xml` to `20.1` + * Replace `99.master` by `${build.closest.tag.name}` in the root `pom.xml` so that debian packages have the right version + * Change the `BTAG` variable in the Makefile to `20.1.x` + * Check the submodule branches in `.gitmodules` are correct, since [dependabot](https://app.dependabot.com/accounts/georchestra/) depends on it to update submodules + * Setup a [new dependabot job](https://app.dependabot.com/accounts/georchestra/) which takes care of updating the submodules for this new branch + * Change the default branches in github repositories Commit and propagate the changes: - ``` git add geonetwork -git commit -am "13.12 release" -git tag v13.12 -git push origin master --tags +git commit -am "20.1.x branch" ``` -Now, let's create the maintenance branch for geOrchestra 13.12: - +When the release is ready on branch `20.1.x`, push a tag: ``` -git checkout -b 13.12 -git push origin 13.12 +git tag 20.1.0 +git push origin 20.1.x --tags ``` -... and update the project version in master: - +The master branch requires some work too: ``` git checkout master -find ./ -name pom.xml -exec sed -i 's/13.12/14.06-SNAPSHOT/' {} \; +find ./ -name pom.xml -exec sed -i 's#20.1-SNAPSHOT#20.2-SNAPSHOT#' {} \; git submodule foreach 'git reset --hard' git commit -am "updated project version in pom.xml" ``` -Let's update GN submodule too: +geOrchestra 20.1.0 is now released, congrats ! + +Do not forget to : + * update the [website](http://www.georchestra.org/software.html) + * [tweet](https://twitter.com/georchestra) ! + +## Patch releases + +We're taking example on the 20.0.2 release. + +### GeoNetwork ``` cd geonetwork -git fetch origin -git checkout georchestra-14.06 -git pull origin georchestra-14.06 -cd - +git checkout georchestra-gn3.8.2 +git pull origin georchestra-gn3.8.2 +git tag 20.0.2 +git push origin 20.0.2 ``` -Commit and propagate the changes: +Then [create a new release](https://github.com/georchestra/geonetwork/releases). + +### geOrchestra + +Create the release commit: ``` +cd georchestra +git checkout 20.0.x +git pull origin 20.0.x +find ./ -name pom.xml -exec sed -i 's#20.0-SNAPSHOT#20.0.2#' {} \; +cd geonetwork +git fetch origin +git checkout 20.0.2 +cd .. git add geonetwork -git commit -m "updated GeoNetwork submodule" -git push origin master +git add -p +git commit -m "20.0.2 release" +git push origin 20.0.x +git tag 20.0.2 +git push origin 20.0.2 +``` + +Then [create a new release](https://github.com/georchestra/georchestra/releases). + +Finally, revert the maven version back to SNAPSHOT: +``` +find ./ -name pom.xml -exec sed -i 's#20.0.2#20.0-SNAPSHOT#' {} \; +git add -p +git commit -m "back to SNAPSHOT" +git push origin 20.0.x ``` -geOrchestra 13.12 is now released, congrats ! +Create new milestones for [georchestra](https://github.com/georchestra/georchestra/milestones) and [geonetwork](https://github.com/georchestra/geonetwork/milestones). -Finally, change the default branch to latest stable in the [georchestra](https://github.com/georchestra/georchestra/settings), [geonetwork](https://github.com/georchestra/geonetwork/settings), [template](https://github.com/georchestra/template/settings) and [LDAP](https://github.com/georchestra/LDAP/settings) repositories. -... and eventually in the geoserver and geofence repositories too. +[Tweet](https://twitter.com/georchestra) ! diff --git a/analytics/.gitignore b/analytics/.gitignore deleted file mode 100644 index ada9fd1fb6..0000000000 --- a/analytics/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -src/main/webapp/header.jsp -src/main/webapp/js/app/config.js -src/main/webapp/WEB-INF/analytics.properties \ No newline at end of file diff --git a/analytics/README.md b/analytics/README.md index 8b0bc763a6..d1d2926595 100644 --- a/analytics/README.md +++ b/analytics/README.md @@ -1,27 +1,15 @@ Analytics ========= -Analytics is a GUI which displays **monthly** and **global** statistics on platform usage: **GeoNetwork downloads**, **custom data extractions** & **OGC web services consumption**. +![analytics](https://github.com/georchestra/georchestra/workflows/analytics/badge.svg) +Analytics offers + * services which are used by the console + * a GUI which displays **monthly** and **global** statistics on platform usage, through OGC web services monitoring. -It relies on two other modules to collect figures in a database: - * [downloadform](downloadform/README.md) for GeoNetwork downloads & custom data extractions, - * [ogc-server-statistics](ogc-server-statistics/README.md) for OGC web services consumption - - -With regards to **GeoNetwork downloads**, you get a nice view of: - * metadata id, filename, number of hits - * username, number of downloads - * organisation, number of downloads - -For **custom data extractions**: - * service type, service URL, layer name, hits - * username, number of requests - * organisation, number of requests - -For **OGC web services consumption**: +It relies on the [ogc-server-statistics](ogc-server-statistics/README.md) module (which is embedded into the security-proxy) to collect figures in a database: * service type, layer name, request type (getmap/getfeature/getcapabilities/...), hits * username, number of requests * organisation, number of requests -Each table can be exported to CSV for easy offline use. \ No newline at end of file +Each table can be exported to CSV for easy offline use. diff --git a/analytics/pom.xml b/analytics/pom.xml index eb8dd3eb81..2f8e633b57 100644 --- a/analytics/pom.xml +++ b/analytics/pom.xml @@ -4,47 +4,66 @@ org.georchestra root - 15.12-SNAPSHOT + 21.0-SNAPSHOT analytics war Analytics webapp Analytics is a GUI which displays monthly and global statistics on platform usage: - GeoNetwork downloads, custom data extractions and OGC web services consumption. + OGC web services consumption. http://www.georchestra.org - 3.1.0.RELEASE + ${project.artifactId} - org.jdom - jdom - 1.1 + org.webjars + extjs org.springframework spring-webmvc - ${spring.version} compile + + org.springframework + spring-test + test + + + org.mockito + mockito-all + test + + + org.hamcrest + hamcrest-all + test + + + javax.validation + validation-api + + org.json json - 20080701 + + + org.jsondoc + jsondoc-core log4j apache-log4j-extras - 1.1 jar runtime org.tuckey urlrewritefilter - 3.0.4 commons-logging @@ -54,182 +73,239 @@ log4j log4j + + javax.servlet + servlet-api + log4j log4j - 1.2.16 jar junit junit - 4.8 test - - org.apache.httpcomponents - httpclient - 4.1.2 - javax.servlet jstl - 1.1.2 compile javax.servlet - servlet-api - 2.5 + javax.servlet-api provided taglibs standard - 1.1.2 - postgresql + org.postgresql postgresql - 9.1-901.jdbc4 - commons-dbcp - commons-dbcp - 1.2.2 + com.mchange + c3p0 - commons-pool - commons-pool - 1.2 - - - commons-io - commons-io - 1.4 + org.slf4j + slf4j-log4j12 org.georchestra georchestra-commons ${project.version} + + joda-time + joda-time + - analytics + ${project.artifactId}-${server} - org.apache.maven.plugins - maven-dependency-plugin - - - org.codehaus.groovy.maven - gmaven-plugin - - - org.georchestra - config - ${project.version} - ${server} - - + net.revelc.code.formatter + formatter-maven-plugin org.apache.maven.plugins maven-war-plugin - - analytics - **/docs/**,js/lib/external/ext/examples/**,js/lib/external/ext/builds/**,js/lib/external/ext/pkgs/**,PostTreatment.groovy - - - - maven-antrun-plugin - - - serverConfigCopy - generate-sources - - - - - - - - - - - - - - - - - run - - - org.eclipse.jetty jetty-maven-plugin - 9.2.11.v20150529 + 9.4.27.v20200227 /analytics/ 5 8280 + + + georchestra.datadir + /etc/georchestra + + - debianPackage + apidoc + + + org.jsondoc + jsondoc-ui + 1.2.22 + + org.apache.maven.plugins - maven-war-plugin - 2.6 + maven-dependency-plugin + + + unpack-jsondoc + process-resources + + unpack + + + + + org.jsondoc + jsondoc-ui + jar + true + ${project.build.directory}/jsondoc-ui + + + + + + + + org.jsondoc + jsondoc-maven-plugin + 1.2.23 + + + org.jsondoc + jsondoc-springmvc + 1.2.23 + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + + generate-jsondoc + package + + generate + + + 4.1 + http://localhost:8080/api + + org.georchestra.analytics + + ${project.build.directory}/jsondoc-ui/jsondoc.json + org.jsondoc.springmvc.scanner.Spring4JSONDocScanner + false + + + + + + com.google.code.maven-replacer-plugin + replacer + 1.5.3 + + + package + + replace + + + - **/docs/**,js/lib/external/ext/examples/**,js/lib/external/ext/builds/**,js/lib/external/ext/pkgs/**,PostTreatment.groovy - generic + + ${project.build.directory}/jsondoc-ui/jsondoc-ui.html + + '_JSONDOC_OFFLINE_PLACEHOLDER_' + ${project.build.directory}/jsondoc-ui/jsondoc.json + + + + + debianPackage + + ${project.artifactId}-generic + - maven-resources-plugin - 2.3 + org.apache.maven.plugins + maven-scm-plugin + 1.11.2 + + ${project.build.directory}/deb/etc/georchestra + scm:git:https://github.com/georchestra/datadir.git + false + ${packageDatadirScmVersion} + branch + - copy-deb-resources + checkout-deb-default-datadir process-resources - copy-resources - - true - ${basedir}/target/deb - - - src/deb/resources - - - + + checkout + org.apache.maven.plugins maven-antrun-plugin - 1.6 + 1.7 + + remove-useless-directories + package + + + + + + + + + + + + run + + fix-permissions package - + @@ -249,10 +325,11 @@ georchestra-analytics geOrchestra Analytics webapp + ${project.packageVersion} geOrchestra PSC psc@georchestra.org - true + true @@ -261,15 +338,52 @@ rpmPackage + ${project.artifactId}-generic org.apache.maven.plugins - maven-war-plugin - 2.6 + maven-scm-plugin + 1.11.2 - generic - **/docs/**,js/lib/external/ext/examples/**,js/lib/external/ext/builds/**,js/lib/external/ext/pkgs/**,PostTreatment.groovy + ${project.build.directory}/deb/etc/georchestra + scm:git:https://github.com/georchestra/datadir.git + ${packageDatadirScmVersion} + branch + false + + + checkout-deb-default-datadir + process-resources + + checkout + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.6 + + + + + + + + + + + + + remove-useless-directories + package + + run + + + org.codehaus.mojo @@ -304,7 +418,7 @@ / - ${basedir}/src/deb/resources + ${project.build.directory}/deb @@ -314,5 +428,146 @@ + + docker + + georchestra/${project.artifactId}:${project.version} + scm:git:https://github.com/georchestra/datadir.git + docker-${packageDatadirScmVersion} + + + ${project.artifactId} + + + org.apache.maven.plugins + maven-scm-plugin + 1.11.2 + + ${project.build.directory}/datadir/ + ${dockerDatadirScmUrl} + false + ${dockerDatadirScmVersion} + branch + + + + checkout-docker-default-datadir + process-resources + + checkout + + + + + + com.spotify + docker-maven-plugin + 1.2.2 + + ${dockerImageName} + ${project.basedir}/src/docker + + + /var/lib/jetty/webapps/${context.name} + ${project.build.directory}/${context.name} + + + /etc/georchestra + ${project.build.directory}/datadir + ${project.artifactId}/** + + + docker-hub + https://index.docker.io/v1/ + + + + com.google.guava + guava + 28.2-jre + + + + + + + + it + + false + + + + + io.fabric8 + docker-maven-plugin + 0.31.0 + + + prepare-containers + pre-integration-test + + start + + + + + georchestra/database + database + + + database_container.port:5432 + + + true + (?s)database system is ready to accept connections + + + + + + + + + remove-containers + post-integration-test + + stop + + + true + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.2 + + + + integration-test + verify + + + + + + ${project.basedir}/src/main/webapp/WEB-INF + ${project.basedir}/src/main/webapp/WEB-INF/classes + + + localhost + ${database_container.port} + georchestra + georchestra + georchestra + + + + + + diff --git a/analytics/src/deb/resources/etc/georchestra/analytics/analytics.properties b/analytics/src/deb/resources/etc/georchestra/analytics/analytics.properties deleted file mode 100644 index 9c43497658..0000000000 --- a/analytics/src/deb/resources/etc/georchestra/analytics/analytics.properties +++ /dev/null @@ -1,11 +0,0 @@ -# This variable configures the JDBC URL to the database where the statistics -# gathered by the OGC-server-statistics module are stored -dlJdbcUrlOGC=jdbc:postgresql://localhost:5432/georchestra?user=www-data&password=www-data - -# This variable configures the JDBC URL to the database where the download -# requests are stored. This is relevant only if the downloadform module is -# activated). -dlJdbcUrlDLForm=jdbc:postgresql://localhost:5432/georchestra?user=www-data&password=www-data - -language=fr -instance=geOrchestra diff --git a/analytics/src/deb/resources/etc/georchestra/analytics/js/GEOR_custom.js b/analytics/src/deb/resources/etc/georchestra/analytics/js/GEOR_custom.js deleted file mode 100644 index 455507424a..0000000000 --- a/analytics/src/deb/resources/etc/georchestra/analytics/js/GEOR_custom.js +++ /dev/null @@ -1,7 +0,0 @@ -GEOR = { - config: { - HEADER_HEIGHT: 90, - OGC_STATISTICS: true, - DOWNLOAD_FORM: true - } -}; diff --git a/analytics/src/deb/resources/etc/georchestra/analytics/log4j/log4j.properties b/analytics/src/deb/resources/etc/georchestra/analytics/log4j/log4j.properties deleted file mode 100644 index dddd5fc7bf..0000000000 --- a/analytics/src/deb/resources/etc/georchestra/analytics/log4j/log4j.properties +++ /dev/null @@ -1,12 +0,0 @@ -log4j.rootLogger=WARN, R - -log4j.logger.org.georchestra.analytics=WARN, R - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = /tmp/analytics.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = /tmp/analytics.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n - diff --git a/analytics/src/docker/Dockerfile b/analytics/src/docker/Dockerfile new file mode 100644 index 0000000000..580aeea288 --- /dev/null +++ b/analytics/src/docker/Dockerfile @@ -0,0 +1,20 @@ +FROM jetty:9-jre8 + +ENV XMS=256M XMX=1G + +RUN java -jar "$JETTY_HOME/start.jar" --create-startd --add-to-start=jmx,jmx-remote,stats,http-forwarded + +COPY --chown=jetty:jetty . / + +VOLUME [ "/tmp", "/run/jetty" ] + +CMD ["sh", "-c", "exec java \ +-Djava.io.tmpdir=/tmp/jetty \ +-Dgeorchestra.datadir=/etc/georchestra \ +-Xms$XMS -Xmx$XMX \ +-XX:-UsePerfData \ +${JAVA_OPTIONS} \ +-Djetty.httpConfig.sendServerVersion=false \ +-Djetty.jmxremote.rmiregistryhost=0.0.0.0 \ +-Djetty.jmxremote.rmiserverhost=0.0.0.0 \ +-jar /usr/local/jetty/start.jar"] diff --git a/analytics/src/main/filtered-resources/WEB-INF/analytics.properties b/analytics/src/main/filtered-resources/WEB-INF/analytics.properties deleted file mode 100644 index 340ba7a2ac..0000000000 --- a/analytics/src/main/filtered-resources/WEB-INF/analytics.properties +++ /dev/null @@ -1,2 +0,0 @@ -dlJdbcUrlOGC=${dlJdbcUrlOGC} -dlJdbcUrlDLForm=${dlJdbcUrlDLForm} diff --git a/analytics/src/main/filtered-resources/WEB-INF/jsp/index.jsp b/analytics/src/main/filtered-resources/WEB-INF/jsp/index.jsp deleted file mode 100644 index a2a171e275..0000000000 --- a/analytics/src/main/filtered-resources/WEB-INF/jsp/index.jsp +++ /dev/null @@ -1,105 +0,0 @@ -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> -<%@ page language="java" %> -<%@ page import="java.util.*" %> -<%@ page import="org.georchestra.analytics.Utf8ResourceBundle" %> -<%@ page contentType="text/html; charset=UTF-8"%> -<%@ page pageEncoding="UTF-8"%> -<%@ page isELIgnored="false" %> -<%@ page import="org.springframework.web.context.support.WebApplicationContextUtils" %> -<%@ page import="org.springframework.context.ApplicationContext" %> -<%@ page import="org.springframework.web.servlet.support.RequestContextUtils" %> -<%@ page import="org.georchestra.commons.configuration.GeorchestraConfiguration" %> - -<% -String defaultLanguage = null, defaultInstanceName = null, instanceName = null; - -String defaultConfigJs = "resources/js/app/config.js"; -try { - ApplicationContext ctx = RequestContextUtils.getWebApplicationContext(request); - if (ctx.getBean(GeorchestraConfiguration.class).activated()) { - defaultLanguage = ctx.getBean(GeorchestraConfiguration.class).getProperty("language"); - defaultInstanceName = ctx.getBean(GeorchestraConfiguration.class).getProperty("instance"); - defaultConfigJs = "ws/app/js/GEOR_custom.js"; - } -} catch (Exception e) {} - - -String lang = request.getParameter("lang"); -if (lang == null || (!lang.equals("en") && !lang.equals("es") && !lang.equals("fr") && !lang.equals("de"))) { - if (defaultLanguage == null) { - lang = "${language}"; - } else { - lang = defaultLanguage; - } -} - -if (defaultInstanceName == null) { - instanceName = "${instance}"; -} else { - instanceName = defaultInstanceName; -} - -Locale l = new Locale(lang); -ResourceBundle resource = Utf8ResourceBundle.getBundle("analytics.i18n.index",l); -javax.servlet.jsp.jstl.core.Config.set( - request, - javax.servlet.jsp.jstl.core.Config.FMT_LOCALIZATION_CONTEXT, - new javax.servlet.jsp.jstl.fmt.LocalizationContext(resource)); - -%> - - - - - - <fmt:message key="title.analytics"/> - <%= instanceName %> - - - - - - - <%@ include file="header.jsp" %> - - -
- chargement - -
- - - - - - - - - - - - diff --git a/analytics/src/main/java/org/georchestra/analytics/AbstractApplication.java b/analytics/src/main/java/org/georchestra/analytics/AbstractApplication.java index f9f2c6901d..26fa4e360a 100644 --- a/analytics/src/main/java/org/georchestra/analytics/AbstractApplication.java +++ b/analytics/src/main/java/org/georchestra/analytics/AbstractApplication.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + package org.georchestra.analytics; import java.io.OutputStream; @@ -17,19 +36,20 @@ /** * Abstract class for all controllers from the webapp Analytics. + * * @author fgravin * */ public abstract class AbstractApplication { - protected final Log logger = LogFactory.getLog(getClass().getPackage().getName()); + protected final Log logger = LogFactory.getLog(getClass().getPackage().getName()); - int month = 1; - int year = 2012; - int start = 0; - int limit = 25; - String sort = "count DESC"; - String filter = ""; + int month = 1; + int year = 2012; + int start = 0; + int limit = 25; + String sort = "count DESC"; + String filter = ""; private static List allowedProperties = Arrays.asList("service", "layer", "request", // Layer tab "user_name", // User tab @@ -39,181 +59,184 @@ public abstract class AbstractApplication { private static List allowedDirections = Arrays.asList("ASC", "DESC"); - /** - * Checks all the parameters from the request and fill class attributes. - * Return false if any parameters is missing or malformed. - * - * @param request - * @param msg - * @return - */ - protected boolean getAllParameters(HttpServletRequest request, StringBuilder msg) { - - try { - if(!getDateParameters(request)) return false; - - start = Integer.valueOf(request.getParameter("start")); - limit = Integer.valueOf(request.getParameter("limit")); - JSONObject obj = new JSONArray(request.getParameter("sort")).getJSONObject(0); - - if (! allowedProperties.contains(obj.getString("property")) - || ! allowedDirections.contains(obj.getString("direction"))) { - throw new IllegalArgumentException("Unexpected parameters provided"); - } - sort = obj.getString("property") + " " + obj.getString("direction"); - filter = request.getParameter("filter"); - - } catch (JSONException e) { - msg.append("Error in sort JSON format"); - return false; - } catch (NumberFormatException e) { - msg.append("One param is missing"); - return false; - } catch (IllegalArgumentException e) { - return false; - } - return true; - } - - /** - * Check the month and year parameters and fill the class attributes. - * Return false if one's missing or bad formatted - * @param request - * @return - */ - protected boolean getDateParameters(HttpServletRequest request) { - - try { - month = Integer.valueOf(request.getParameter("month")); - year = Integer.valueOf(request.getParameter("year")); - } - catch (NumberFormatException e) { - return false; - } - - if (month<0 || month > 12) { - return false; - } - return true; - } - - /** - * Send the unsuccessful response containing success:false, and the message - * from an error - * - * @param out response stream to write the message in - * @param msg error message - * @throws Exception - */ - protected void sendSuccessFalse(final OutputStream out, final String msg) throws Exception { - JSONObject object = new JSONObject(); - object.put("success", false); - object.put("msg", "invalid params => " + msg); - out.write(object.toString().getBytes()); - } - - /** - * Report an error if exception is thrown during the SQL or JSON process. - * - * @param out response stream to write an error message - * @param response - * @param e - * @throws Exception - */ - protected void reportError(final OutputStream out, HttpServletResponse response, Exception e) throws Exception { - if (out != null) { - out.write("Internal Server Error: unable to handle request.".getBytes()); - } - logger.error("Caught exception while executing service: ", e); - response.setStatus(500); - } - - /** - * Updates the response ContentType and header to make the browser download the CSV file - * - * @param csv the CSV content as String - * @param filename the filename the CSV will be saved under - * @param response - * @throws Exception - */ - protected void respondCSV(String csv, final String filename, HttpServletResponse response) throws Exception { - response.setContentType("text/csv"); - response.setContentLength(csv.getBytes().length); - response.setHeader("Content-Disposition", "attachment; filename=\""+filename+CSVUtil.CSV_EXT+"\""); - response.getWriter().write(csv); - } - - /** - * Generic method from all WS. Will call the strategy.process method which refer to the - * WS' model. This model will return result as JSONObject. This object will be returned - * in response. If no error occurs, the response will contain success:true, the total - * number of results, and an array of results. - * If an exception is thrown during the request, a JSON response with success:false will be returned. - * If an error occurs during SQL or JSON process, an exception will be thrown. - * - * @param request - * @param response - * @param strategy contains the method to call the model and retrieve the results - * @throws Exception - */ - protected void getStats(HttpServletRequest request, HttpServletResponse response, StrategyController strategy) throws Exception { - - OutputStream out = response.getOutputStream(); - StringBuilder msg = new StringBuilder(); - - if(!this.getAllParameters(request, msg)) { - sendSuccessFalse(out, msg.toString()); - } - else { - try { - JSONObject object = strategy.process(); - out.write(object.toString().getBytes()); - - } catch (Exception e) { - reportError(out, response, e); - } - } - } - - /** - * Generic method from all WS. Will call the strategy.process method which refers to the - * WS' model. This model will return result as JSONObject. This object will then be - * parsed to write a CSV file which will be returned in the response, using response header as - * CSV file. - * - * @param request - * @param response - * @param strategy contain the method to call the model an retrieve results - * @throws Exception - */ - protected void exportCSV(HttpServletRequest request, HttpServletResponse response, String csvFileName, StrategyController strategy) throws Exception { - - if(!this.getDateParameters(request)) { - OutputStream out = response.getOutputStream(); - sendSuccessFalse(out, "Invalid parameters"); - } - else { - String csv=null; - try { - JSONObject object = strategy.process(); - csv = CSVUtil.JSONToCSV(object); - csvFileName = String.valueOf(year) + "-" + String.format("%02d", month) + "-" + csvFileName; - - } catch (Exception e) { - OutputStream out = response.getOutputStream(); - reportError(out, response, e); - } - respondCSV(csv, csvFileName, response); - } - } - - /** - * Inner Abstract class to pass the call of the specific model to the generic - * getStats and exportCSV methods. - * - */ - protected abstract class StrategyController { - - protected abstract JSONObject process() throws SQLException, JSONException; - } + /** + * Checks all the parameters from the request and fill class attributes. Return + * false if any parameters is missing or malformed. + * + * @param request + * @param msg + * @return + */ + protected boolean getAllParameters(HttpServletRequest request, StringBuilder msg) { + + try { + if (!getDateParameters(request)) + return false; + + start = Integer.valueOf(request.getParameter("start")); + limit = Integer.valueOf(request.getParameter("limit")); + JSONObject obj = new JSONArray(request.getParameter("sort")).getJSONObject(0); + + if (!allowedProperties.contains(obj.getString("property")) + || !allowedDirections.contains(obj.getString("direction"))) { + throw new IllegalArgumentException("Unexpected parameters provided"); + } + sort = obj.getString("property") + " " + obj.getString("direction"); + filter = request.getParameter("filter"); + + } catch (JSONException e) { + msg.append("Error in sort JSON format"); + return false; + } catch (NumberFormatException e) { + msg.append("One param is missing"); + return false; + } catch (IllegalArgumentException e) { + return false; + } + return true; + } + + /** + * Check the month and year parameters and fill the class attributes. Return + * false if one's missing or bad formatted + * + * @param request + * @return + */ + protected boolean getDateParameters(HttpServletRequest request) { + + try { + month = Integer.valueOf(request.getParameter("month")); + year = Integer.valueOf(request.getParameter("year")); + } catch (NumberFormatException e) { + return false; + } + + if (month < 0 || month > 12) { + return false; + } + return true; + } + + /** + * Send the unsuccessful response containing success:false, and the message from + * an error + * + * @param out response stream to write the message in + * @param msg error message + * @throws Exception + */ + protected void sendSuccessFalse(final OutputStream out, final String msg) throws Exception { + JSONObject object = new JSONObject(); + object.put("success", false); + object.put("msg", "invalid params => " + msg); + out.write(object.toString().getBytes()); + } + + /** + * Report an error if exception is thrown during the SQL or JSON process. + * + * @param out response stream to write an error message + * @param response + * @param e + * @throws Exception + */ + protected void reportError(final OutputStream out, HttpServletResponse response, Exception e) throws Exception { + if (out != null) { + out.write("Internal Server Error: unable to handle request.".getBytes()); + } + logger.error("Caught exception while executing service: ", e); + response.setStatus(500); + } + + /** + * Updates the response ContentType and header to make the browser download the + * CSV file + * + * @param csv the CSV content as String + * @param filename the filename the CSV will be saved under + * @param response + * @throws Exception + */ + protected void respondCSV(String csv, final String filename, HttpServletResponse response) throws Exception { + response.setContentType("text/csv"); + response.setContentLength(csv.getBytes().length); + response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + CSVUtil.CSV_EXT + "\""); + response.getWriter().write(csv); + } + + /** + * Generic method from all WS. Will call the strategy.process method which refer + * to the WS' model. This model will return result as JSONObject. This object + * will be returned in response. If no error occurs, the response will contain + * success:true, the total number of results, and an array of results. If an + * exception is thrown during the request, a JSON response with success:false + * will be returned. If an error occurs during SQL or JSON process, an exception + * will be thrown. + * + * @param request + * @param response + * @param strategy contains the method to call the model and retrieve the + * results + * @throws Exception + */ + protected void getStats(HttpServletRequest request, HttpServletResponse response, StrategyController strategy) + throws Exception { + + OutputStream out = response.getOutputStream(); + StringBuilder msg = new StringBuilder(); + + if (!this.getAllParameters(request, msg)) { + sendSuccessFalse(out, msg.toString()); + } else { + try { + JSONObject object = strategy.process(); + out.write(object.toString().getBytes()); + + } catch (Exception e) { + reportError(out, response, e); + } + } + } + + /** + * Generic method from all WS. Will call the strategy.process method which + * refers to the WS' model. This model will return result as JSONObject. This + * object will then be parsed to write a CSV file which will be returned in the + * response, using response header as CSV file. + * + * @param request + * @param response + * @param strategy contain the method to call the model an retrieve results + * @throws Exception + */ + protected void exportCSV(HttpServletRequest request, HttpServletResponse response, String csvFileName, + StrategyController strategy) throws Exception { + + if (!this.getDateParameters(request)) { + OutputStream out = response.getOutputStream(); + sendSuccessFalse(out, "Invalid parameters"); + } else { + String csv = null; + try { + JSONObject object = strategy.process(); + csv = CSVUtil.JSONToCSV(object); + csvFileName = String.valueOf(year) + "-" + String.format("%02d", month) + "-" + csvFileName; + respondCSV(csv, csvFileName, response); + } catch (Exception e) { + OutputStream out = response.getOutputStream(); + reportError(out, response, e); + } + } + } + + /** + * Inner Abstract class to pass the call of the specific model to the generic + * getStats and exportCSV methods. + * + */ + protected abstract class StrategyController { + + protected abstract JSONObject process() throws SQLException, JSONException; + } } \ No newline at end of file diff --git a/analytics/src/main/java/org/georchestra/analytics/DefaultController.java b/analytics/src/main/java/org/georchestra/analytics/DefaultController.java index 955574a25c..d34c5eaa79 100644 --- a/analytics/src/main/java/org/georchestra/analytics/DefaultController.java +++ b/analytics/src/main/java/org/georchestra/analytics/DefaultController.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + package org.georchestra.analytics; import org.springframework.stereotype.Controller; @@ -8,8 +27,8 @@ @Controller public class DefaultController { - @RequestMapping(value="/*", method=RequestMethod.GET) - public ModelAndView index(){ + @RequestMapping(value = "/*", method = RequestMethod.GET) + public ModelAndView index() { ModelAndView mv = new ModelAndView("index"); return mv; } diff --git a/analytics/src/main/java/org/georchestra/analytics/ExtractorStats.java b/analytics/src/main/java/org/georchestra/analytics/ExtractorStats.java deleted file mode 100644 index 20a6dbc21c..0000000000 --- a/analytics/src/main/java/org/georchestra/analytics/ExtractorStats.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.georchestra.analytics; - -import java.sql.SQLException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.georchestra.analytics.model.ExtractorStatsModel; -import org.json.JSONException; -import org.json.JSONObject; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - * Extractor controller - * - * @author: fgravin - */ - -@Controller -public class ExtractorStats extends AbstractApplication { - - protected ExtractorStats(ExtractorStatsModel model) { - this.model = model; - } - private ExtractorStatsModel model; - - private final String csvLayers= "ExtractorLayers"; - private final String csvUsers= "ExtractorUsers"; - private final String csvGroups= "ExtractorGroups"; - - @RequestMapping(method = RequestMethod.GET, value = "/extractor/layers") - public void getLayersStats(HttpServletRequest request, HttpServletResponse response) throws Exception { - - getStats(request, response, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getLayersStats(month, year, start, limit, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/extractor/users") - public void getUsersStats(HttpServletRequest request, HttpServletResponse response) throws Exception { - - getStats(request, response, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getUsersStats(month, year, start, limit, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/extractor/groups") - public void getGroupsStats(HttpServletRequest request, HttpServletResponse response) throws Exception { - - getStats(request, response, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getGroupsStats(month, year, start, limit, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/export/extractorlayers") - public void exportLayers(HttpServletRequest request, HttpServletResponse response) throws Exception { - - exportCSV(request, response, csvLayers, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getLayersStats(month, year, 0, Integer.MAX_VALUE, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/export/extractorusers") - public void exportUsers(HttpServletRequest request, HttpServletResponse response) throws Exception { - - exportCSV(request, response, csvUsers, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getUsersStats(month, year, 0, Integer.MAX_VALUE, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/export/extractorgroups") - public void exportGroups(HttpServletRequest request, HttpServletResponse response) throws Exception { - - exportCSV(request, response, csvGroups, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getGroupsStats(month, year, 0, Integer.MAX_VALUE, sort, filter); - } - }); - } -} \ No newline at end of file diff --git a/analytics/src/main/java/org/georchestra/analytics/GeonetworkStats.java b/analytics/src/main/java/org/georchestra/analytics/GeonetworkStats.java deleted file mode 100644 index a18aec7def..0000000000 --- a/analytics/src/main/java/org/georchestra/analytics/GeonetworkStats.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.georchestra.analytics; - -import java.sql.SQLException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.georchestra.analytics.model.GeonetworkStatsModel; -import org.json.JSONException; -import org.json.JSONObject; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - * Geonetwork controller - * - * @author: fgravin - */ - -@Controller -public class GeonetworkStats extends AbstractApplication { - - protected GeonetworkStats(GeonetworkStatsModel model) { - this.model = model; - } - - private GeonetworkStatsModel model; - - private final String csvFiles= "GeonetworkFiles"; - private final String csvUsers= "GeonetworkUsers"; - private final String csvGroups= "GeonetworkGroups"; - - @RequestMapping(method = RequestMethod.GET, value = "/geonetwork/files") - public void getOGCLayersStats(HttpServletRequest request, HttpServletResponse response) throws Exception { - - getStats(request, response, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getFilesStats(month, year, start, limit, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/geonetwork/users") - public void getOGCUsersStats(HttpServletRequest request, HttpServletResponse response) throws Exception { - - getStats(request, response, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getUsersStats(month, year, start, limit, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/geonetwork/groups") - public void getOGCGroupsStats(HttpServletRequest request, HttpServletResponse response) throws Exception { - - getStats(request, response, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getGroupsStats(month, year, start, limit, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/export/geonetworkfiles") - public void exportLayers(HttpServletRequest request, HttpServletResponse response) throws Exception { - - exportCSV(request, response, csvFiles, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getFilesStats(month, year, 0, Integer.MAX_VALUE, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/export/geonetworkusers") - public void exportUsers(HttpServletRequest request, HttpServletResponse response) throws Exception { - - exportCSV(request, response, csvUsers, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getUsersStats(month, year, 0, Integer.MAX_VALUE, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/export/geonetworkgroups") - public void exportGroups(HttpServletRequest request, HttpServletResponse response) throws Exception { - - exportCSV(request, response, csvGroups, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getGroupsStats(month, year, 0, Integer.MAX_VALUE, sort, filter); - } - }); - } -} \ No newline at end of file diff --git a/analytics/src/main/java/org/georchestra/analytics/GeorCustomController.java b/analytics/src/main/java/org/georchestra/analytics/GeorCustomController.java new file mode 100644 index 0000000000..899ff067bc --- /dev/null +++ b/analytics/src/main/java/org/georchestra/analytics/GeorCustomController.java @@ -0,0 +1,26 @@ +package org.georchestra.analytics; + +import org.georchestra.commons.configuration.GeorchestraConfiguration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Controller +public class GeorCustomController { + @Autowired + private GeorchestraConfiguration georConfig; + + /** + * JS configuration entry point. + * + * This end point is served by commons as it is also used in extractorapp + */ + @RequestMapping("/app/js/GEOR_custom.js") + public void getGeorCustom(HttpServletRequest request, HttpServletResponse response) throws Exception { + this.georConfig.getGeorCustom(request, response); + } + +} diff --git a/analytics/src/main/java/org/georchestra/analytics/HomeController.java b/analytics/src/main/java/org/georchestra/analytics/HomeController.java index 1ed9b83a97..72612b1b6d 100644 --- a/analytics/src/main/java/org/georchestra/analytics/HomeController.java +++ b/analytics/src/main/java/org/georchestra/analytics/HomeController.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + package org.georchestra.analytics; import java.io.IOException; @@ -12,10 +31,10 @@ @Controller public class HomeController { - - @RequestMapping(value = "/index.jsp", method=RequestMethod.GET) + + @RequestMapping(value = "/index.jsp", method = RequestMethod.GET) public ModelAndView handleIndex(HttpServletRequest request, HttpServletResponse response) throws IOException { return new ModelAndView("index"); } - + } \ No newline at end of file diff --git a/analytics/src/main/java/org/georchestra/analytics/OGCStats.java b/analytics/src/main/java/org/georchestra/analytics/OGCStats.java index 171b87fca1..121b27d20b 100644 --- a/analytics/src/main/java/org/georchestra/analytics/OGCStats.java +++ b/analytics/src/main/java/org/georchestra/analytics/OGCStats.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + package org.georchestra.analytics; import java.sql.SQLException; @@ -14,80 +33,80 @@ /** * ogc layers controller - * + * * @author: fgravin */ @Controller public class OGCStats extends AbstractApplication { - protected OGCStats(OGCStatsModel model) { - this.model = model; - } - - private OGCStatsModel model; - - private final String csvLayers= "OgcLayers"; - private final String csvUsers= "OgcUsers"; - private final String csvGroups= "OgcGroups"; - - @RequestMapping(method = RequestMethod.GET, value = "/ogc/layers") - public void getOGCLayersStats(HttpServletRequest request, HttpServletResponse response) throws Exception { - - getStats(request, response, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getLayersStats(month, year, start, limit, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/ogc/users") - public void getOGCUsersStats(HttpServletRequest request, HttpServletResponse response) throws Exception { - - getStats(request, response, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getUsersStats(month, year, start, limit, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/ogc/groups") - public void getOGCGroupsStats(HttpServletRequest request, HttpServletResponse response) throws Exception { - - getStats(request, response, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getGroupsStats(month, year, start, limit, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/export/ogclayers") - public void exportLayers(HttpServletRequest request, HttpServletResponse response) throws Exception { - - exportCSV(request, response, csvLayers, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getLayersStats(month, year, 0, Integer.MAX_VALUE, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/export/ogcusers") - public void exportUsers(HttpServletRequest request, HttpServletResponse response) throws Exception { - - exportCSV(request, response, csvUsers, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getUsersStats(month, year, 0, Integer.MAX_VALUE, sort, filter); - } - }); - } - - @RequestMapping(method = RequestMethod.GET, value = "/export/ogcgroups") - public void exportGroups(HttpServletRequest request, HttpServletResponse response) throws Exception { - - exportCSV(request, response, csvGroups, new StrategyController(){ - protected JSONObject process() throws SQLException, JSONException { - return model.getGroupsStats(month, year, 0, Integer.MAX_VALUE, sort, filter); - } - }); - } -} \ No newline at end of file + protected OGCStats(OGCStatsModel model) { + this.model = model; + } + + private OGCStatsModel model; + + private final String csvLayers = "OgcLayers"; + private final String csvUsers = "OgcUsers"; + private final String csvOrgs = "OgcOrgs"; + + @RequestMapping(method = RequestMethod.GET, value = "/ogc/layers") + public void getOGCLayersStats(HttpServletRequest request, HttpServletResponse response) throws Exception { + + getStats(request, response, new StrategyController() { + protected JSONObject process() throws SQLException, JSONException { + return model.getLayersStats(month, year, start, limit, sort, filter); + } + }); + } + + @RequestMapping(method = RequestMethod.GET, value = "/ogc/users") + public void getOGCUsersStats(HttpServletRequest request, HttpServletResponse response) throws Exception { + + getStats(request, response, new StrategyController() { + protected JSONObject process() throws SQLException, JSONException { + return model.getUsersStats(month, year, start, limit, sort, filter); + } + }); + } + + @RequestMapping(method = RequestMethod.GET, value = "/ogc/orgs") + public void getOGCOrgsStats(HttpServletRequest request, HttpServletResponse response) throws Exception { + + getStats(request, response, new StrategyController() { + protected JSONObject process() throws SQLException, JSONException { + return model.getOrgsStats(month, year, start, limit, sort, filter); + } + }); + } + + @RequestMapping(method = RequestMethod.GET, value = "/export/ogclayers") + public void exportLayers(HttpServletRequest request, HttpServletResponse response) throws Exception { + + exportCSV(request, response, csvLayers, new StrategyController() { + protected JSONObject process() throws SQLException, JSONException { + return model.getLayersStats(month, year, 0, Integer.MAX_VALUE, sort, filter); + } + }); + } + + @RequestMapping(method = RequestMethod.GET, value = "/export/ogcusers") + public void exportUsers(HttpServletRequest request, HttpServletResponse response) throws Exception { + + exportCSV(request, response, csvUsers, new StrategyController() { + protected JSONObject process() throws SQLException, JSONException { + return model.getUsersStats(month, year, 0, Integer.MAX_VALUE, sort, filter); + } + }); + } + + @RequestMapping(method = RequestMethod.GET, value = "/export/ogcorgs") + public void exportOrgs(HttpServletRequest request, HttpServletResponse response) throws Exception { + + exportCSV(request, response, csvOrgs, new StrategyController() { + protected JSONObject process() throws SQLException, JSONException { + return model.getOrgsStats(month, year, 0, Integer.MAX_VALUE, sort, filter); + } + }); + } +} diff --git a/analytics/src/main/java/org/georchestra/analytics/StatisticsController.java b/analytics/src/main/java/org/georchestra/analytics/StatisticsController.java new file mode 100644 index 0000000000..5a9defad79 --- /dev/null +++ b/analytics/src/main/java/org/georchestra/analytics/StatisticsController.java @@ -0,0 +1,828 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.analytics; + +import java.beans.PropertyVetoException; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.text.ParseException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletResponse; +import javax.sql.DataSource; + +import org.georchestra.analytics.util.QueryBuilder; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.Duration; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.jsondoc.core.annotation.Api; +import org.jsondoc.core.annotation.ApiMethod; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.google.common.annotations.VisibleForTesting; + +/** + * This controller defines the entry point to return statistics based on user or + * orgs, for a given date period. + * + * The entry point "/combinedRequests" receives a plain JSON object defined as + * follows: + * + * - search by user: + * + *
+ * {
+ *   "user": "user",
+ *   "startDate": "YYY-mm-dd",
+ *   "endDate": "YY-mm-dd"
+ * }
+ * 
+ * + * - search by roles: + * + *
+ * {
+ *   "role": "role",
+ *   "startDate": "YYY-mm-dd",
+ *   "endDate": "YY-mm-dd"
+ * }
+ * 
+ * + * It will return a JSON object which follows the current format: + * + *
+ * {
+ *   "granularity": "GRANULARITY",
+ *   "results": [
+ *     {
+ *       "count": int,
+ *       "date": "YYY-mm-dd"
+ *     }, ...
+ *    ]
+ * }
+ * 
+ * + * If neither user nor role is set, global statistics are returned. + * + * where granularity will depend on the submitted date, following the algorithm: + * if datediff < 2 days then granularity by hour if datediff < 1 week then + * granularity by day if datediff < 1 month then granularity by day if datediff + * < 3 months then granularity by week if datediff < 1 year then granularity by + * month + * + * - The entry point "/layersUsage" receives a JSON object as follows: + * + *
+ * {
+ *   "user"|"role": "user|role",
+ *   "limit": integer,
+ *   "startDate": "YYYY-mm-dd",
+ *   "endDate": "YYYY-mm-dd"
+ * }
+ * 
+ * + * User, role and limit are optional parameters. + * + * The returned JSON object will follow the pattern: + * + *
+ * { "results": [
+ *   {
+ *       "count": 831,
+ *       "layer": "layername1"
+ *   },
+ *   {
+ *       "count": 257,
+ *       "layer": "layername2"
+ *   }, ...
+ *   ]
+ * }
+ * 
+ * + * - the entry point "/distinctUsers" receives a JSON object as follows: + * + *
+ * {
+ *   "role": "role",
+ *   "startDate": "YYYY-mm-dd",
+ *   "endDate": "YYY-mm-dd"
+ * }
+ * 
+ * + * role is optional. If not set, global statistics are returned. + * + * The returned object will follow the pattern: + * + *
+ * {
+ *   "results": [
+ *     { "user": "user1", "nb_requests": 10, "organization": "truite" },
+ *     { "user": "user1", "nb_requests": 10, "organization": "truite" },
+ *     ...
+ *   ]
+ * }
+ * 
+ * + * @author pmauduit + * @since 15.12 + */ + +@Controller +@Api(name = "Statistics API", description = "Methods to get several statistics " + + "related to users and roles, and their use of the infrastructure.") +public class StatisticsController { + + @Autowired + private DataSource dataSource; + + private final QueryBuilder queryBuilder = new QueryBuilder(); + + private DateTimeFormatter localInputFormatter; + private DateTimeFormatter dbOutputFormatter; + + private DateTimeFormatter dbHourInputFormatter; + private DateTimeFormatter dbHourOutputFormatter; + private DateTimeFormatter dbDayOutputFormatter; + private DateTimeFormatter dbWeekInputFormatter; + private DateTimeFormatter dbWeekOutputFormatter; + private DateTimeFormatter dbMonthInputFormatter; + private DateTimeFormatter dbMonthOutputFormatter; + private DateTimeFormatter dbDayInputFormatter; + + // List of users to ignore in stats + private Set excludedUsers; + + private static enum FORMAT { + JSON, CSV + } + + private static enum REQUEST_TYPE { + USAGE, EXTRACTION + } + + public StatisticsController(String localTimezone) throws PropertyVetoException, SQLException { + // Parser to convert from local time to DB time (UTC) + this.localInputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd").withZone(DateTimeZone.forID(localTimezone)); + this.dbOutputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").withZone(DateTimeZone.forID("UTC")); + + // Used to parse date from DB based on granularity + this.dbHourInputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH").withZone(DateTimeZone.forID("UTC")); + this.dbHourOutputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH") + .withZone(DateTimeZone.forID(localTimezone)); + + this.dbDayInputFormatter = DateTimeFormat.forPattern("y-M-d").withZone(DateTimeZone.forID("UTC")); + this.dbDayOutputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd").withZone(DateTimeZone.forID(localTimezone)); + + this.dbWeekInputFormatter = DateTimeFormat.forPattern("y-w").withZone(DateTimeZone.forID("UTC")); + this.dbWeekOutputFormatter = DateTimeFormat.forPattern("yyyy-ww").withZone(DateTimeZone.forID(localTimezone)); + + this.dbMonthInputFormatter = DateTimeFormat.forPattern("y-M").withZone(DateTimeZone.forID("UTC")); + this.dbMonthOutputFormatter = DateTimeFormat.forPattern("yyyy-MM").withZone(DateTimeZone.forID(localTimezone)); + } + + // Getter and setter for unit tests + + public @VisibleForTesting void setDataSource(DataSource ds) { + this.dataSource = ds; + } + + public void setExcludedUsers(Set excludedUsers) { + excludedUsers.add("anonymousUser"); + this.excludedUsers = excludedUsers; + } + + /** + * Granularity used for the returned date type in combined requests statistics + */ + public static enum GRANULARITY { + HOUR, DAY, WEEK, MONTH + } + + /* + * Test examples : + * + * combinedRequests with user: curl -XPOST --data-binary '{"user": "testadmin", + * "startDate": "2015-01-01", "endDate": "2015-12-01" }' \ -H'Content-Type: + * application/json' http://localhost:8280/analytics/ws/combinedRequests -i + * + * combinedRequests with role: curl -XPOST --data-binary '{"role": + * "ADMINISTRATOR", "startDate": "2015-10-01", "endDate": "2015-11-01" }' \ + * -H'Content-Type: application/json' + * http://localhost:8280/analytics/ws/combinedRequests -i + * + * layersUsage with user: curl -XPOST --data-binary '{"user": "testadmin", + * "limit": 10, "startDate": "2015-01-01", "endDate": "2015-12-01" }' \ + * -H'Content-Type: application/json' + * http://localhost:8280/analytics/ws/layersUsage -i + * + * layersUsage with role: curl -XPOST --data-binary '{"role": "ADMINISTRATOR", + * "startDate": "2015-01-01", "endDate": "2015-12-01" }' \ -H'Content-Type: + * application/json' http://localhost:8280/analytics/ws/layersUsage -i + * + * layersUsage without filter: curl -XPOST --data-binary '{"limit": 10, + * "startDate": "2015-01-01", "endDate": "2015-12-01" }' \ -H'Content-Type: + * application/json' http://localhost:8280/analytics/ws/layersUsage -i + * + * distinctUsers : curl -XPOST --data-binary '{"role": "ADMINISTRATOR", + * "startDate": "2015-01-01", "endDate": "2015-12-01" }' \ -H'Content-Type: + * application/json' http://localhost:8280/analytics/ws/distinctUsers -i + */ + /** + * Total combined requests count groupped by time interval (hour, day, week or + * month). May be filtered by a user or a role. + * + * @param payload the JSON object containing the input parameters + * @param response the HttpServletResponse object. + * @return a JSON string or CSV doc containing the requested aggregated + * statistics. + * + * @throws JSONException + */ + @RequestMapping(value = "/combinedRequests.{format}", method = RequestMethod.POST) + @ResponseBody + @ApiMethod(description = "Returns the Total combined requests count groupped by time interval " + + "(hour, day, week or month). It must be filtered by either a user or a role. " + + "User or role is mandatory, a startDate and an endDate must be specified, ie:" + "
" + + "{ user: testadmin, startDate: 2015-01-01, endDate: 2015-12-01 }" + "
or
" + "" + + "{ role: ADMINISTRATOR, startDate: 2015-10-01, endDate: 2015-11-01 }" + "
" + + "is a valid request." + "") + public String combinedRequests(@RequestBody String payload, @PathVariable String format, + HttpServletResponse response) throws JSONException, ParseException, SQLException { + + JSONObject input = null; + Map sqlValues = new HashMap<>(); + + // Parse Input + try { + input = new JSONObject(payload); + if (!input.has("startDate") || !input.has("endDate")) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + sqlValues.put("startDate", this.convertLocalDateToUTC(input.getString("startDate"))); + sqlValues.put("endDate", this.convertLocalDateToUTC(input.getString("endDate"))); + + } catch (Throwable e) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + // not both role and user can be defined at the same time + if (input.has("user") && input.has("role")) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + if (input.has("user")) { + sqlValues.put("user", input.getString("user")); + } + if (input.has("role")) { + sqlValues.put("role", "ROLE_" + input.getString("role")); + } + // Compute expression to aggregate dates + GRANULARITY g = this.guessGranularity((String) sqlValues.get("startDate"), (String) sqlValues.get("endDate")); + String aggregateDate; + switch (g) { + case HOUR: + aggregateDate = "YYYY-mm-dd HH24"; + break; + case DAY: + aggregateDate = "YYYY-mm-dd"; + break; + case WEEK: + aggregateDate = "YYYY-IW"; + break; + case MONTH: + aggregateDate = "YYYY-mm"; + break; + default: + throw new IllegalArgumentException("Invalid value for granularity"); + } + sqlValues.put("aggregateDateExpression", aggregateDate); + + // Generate SQL query + String sql = "SELECT COUNT(*) AS count," + + " to_char(date, {aggregateDateExpression}) AS aggregate_date " + + " FROM ogcstatistics.ogc_services_log " + + " WHERE date >= CAST({startDate} AS timestamp without time zone) " + + " AND date < CAST({endDate} AS timestamp without time zone) "; + + // Handle user and role + if (input.has("user")) { + sql += " AND user_name = {user} "; + } + if (input.has("role")) { + sql += " AND {role} = ANY (roles) "; + } + sql += "GROUP BY to_char(date, {aggregateDateExpression}) " + + "ORDER BY to_char(date, {aggregateDateExpression})"; + + // Fetch and format results + final String generatedQuery = queryBuilder.generateQuery(sql, sqlValues); + try (Connection c = dataSource.getConnection(); // + Statement st = c.createStatement(); // + ResultSet res = st.executeQuery(generatedQuery)) { + + response.setCharacterEncoding("utf-8"); + + if ("json".equals(format)) { + response.setContentType("application/json"); + } else if ("csv".equals(format)) { + response.setContentType("application/csv"); + } else { + throw new IllegalArgumentException("Invalid format : " + format); + } + + JSONArray results = new JSONArray(); + StringBuilder csv = new StringBuilder(); + csv.append("date,count\n"); + + while (res.next()) { + String date = this.convertUTCDateToLocal(res.getString("aggregate_date"), g); + int count = res.getInt("count"); + if ("json".equals(format)) { + results.put(new JSONObject().put("count", count).put("date", date)); + } else if ("csv".equals(format)) { + csv.append(date + "," + count + "\n"); + } + } + + if ("json".equals(format)) { + return new JSONObject().put("results", results).put("granularity", g).toString(4); + } else if ("csv".equals(format)) { + return csv.toString(); + } else { + throw new IllegalArgumentException("Invalid format : " + format); + } + } + + } + + /** + * Gets statistics for layers consumption in JSON format. May be filtered by a + * user or a role and limited. + * + * @param payload the JSON object containing the input parameters + * @param response the HttpServletResponse object. + * @return a JSON string containing the requested aggregated statistics. + * + * @throws JSONException + */ + @RequestMapping(value = "/layersUsage.json", method = RequestMethod.POST, produces = "application/json; charset=utf-8") + @ResponseBody + public String layersUsageJson(@RequestBody String payload, HttpServletResponse response) + throws JSONException, SQLException { + return this.generateStats(payload, REQUEST_TYPE.USAGE, response, FORMAT.JSON); + } + + /** + * Gets statistics for layers consumption in CSV format. May be filtered by a + * user or a role and limited. + * + * @param payload the JSON object containing the input parameters + * @param response the HttpServletResponse object. + * @return a CSV string containing the requested aggregated statistics. + * + * @throws JSONException + */ + @RequestMapping(value = "/layersUsage.csv", method = RequestMethod.POST, produces = "application/csv; charset=utf-8") + @ResponseBody + public String layersUsage(@RequestBody String payload, HttpServletResponse response) + throws JSONException, SQLException { + return this.generateStats(payload, REQUEST_TYPE.USAGE, response, FORMAT.CSV); + } + + /** + * Gets statistics for layers extraction in JSON format. May be filtered by a + * user or a role and limited. + * + * @param payload the JSON object containing the input parameters + * @param response the HttpServletResponse object. + * @return a JSON string containing the requested aggregated statistics. + * + * @throws JSONException + */ + @RequestMapping(value = "/layersExtraction.json", method = RequestMethod.POST, produces = "application/json; charset=utf-8") + @ResponseBody + public String layersExtractionJson(@RequestBody String payload, HttpServletResponse response) + throws JSONException, SQLException { + return this.generateStats(payload, REQUEST_TYPE.EXTRACTION, response, FORMAT.JSON); + } + + /** + * Gets full statistics for layers extraction in JSON format. Compared to + * previous method, this method will not aggregate records and it will contains + * several new informations : organization, start date, end date, duration ... + * + * @param startDate minimum date for stats + * @param endDate maximum date for stats + * @param response the HttpServletResponse object. + * @return a JSON string containing the requested statistics. + * + * @throws JSONException + */ + @RequestMapping(value = "/fullLayersExtraction.csv", method = RequestMethod.GET, produces = "application/csv; charset=utf-8") + @ResponseBody + public String fullLayersExtractionStats(@RequestParam String startDate, @RequestParam String endDate, + HttpServletResponse response) throws JSONException, SQLException { + + try { + if (startDate == null || endDate == null) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + } catch (Throwable e) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + response.setHeader("Content-Disposition", "attachment; filename=data.csv"); + response.setContentType("application/csv; charset=utf-8"); + + String sql = "SELECT username, org, creation_date, CAST(duration AS text), creation_date + duration AS start_date, layer_name, is_successful, " + + " trunc(CAST(ST_XMin(bbox) AS numeric), 5) || ',' || trunc(CAST(ST_YMin(bbox) AS numeric), 5) || ',' || " + + " trunc(CAST(ST_XMax(bbox) AS numeric), 5) || ',' || trunc(CAST(ST_YMax(bbox) AS numeric), 5) AS bbox, " + + " ST_Area(CAST(bbox AS geography), TRUE) / 1000000 AS area_km2 " + + " FROM extractorapp.extractor_layer_log " + " LEFT JOIN extractorapp.extractor_log " + + " ON (extractorapp.extractor_log.id = extractorapp.extractor_layer_log.extractor_log_id) " + + " WHERE creation_date >= CAST({startDate} AS timestamp without time zone) AND creation_date < CAST({endDate} AS timestamp without time zone) "; + + Map sqlValues = new HashMap<>(); + sqlValues.put("startDate", startDate); + sqlValues.put("endDate", endDate); + + final String generatedQuery = queryBuilder.generateQuery(sql, sqlValues); + try (Connection c = dataSource.getConnection(); // + Statement st = c.createStatement(); // + ResultSet sqlRes = st.executeQuery(generatedQuery)) { + + StringBuilder res = new StringBuilder( + "username;organization;creation_date;duration;end_date;layer_name;is_successful;bbox;area_km2\n"); + while (sqlRes.next()) { + for (int i = 1; i < 9; i++) + res.append(sqlRes.getString(i) + ";"); + res.append(sqlRes.getString(9) + "\n"); + } + return res.toString(); + } + } + + /** + * Gets statistics for layers extraction in CSV format. May be filtered by a + * user or a role and limited. + * + * @param payload the JSON object containing the input parameters + * @param response the HttpServletResponse object. + * @return a CSV string containing the requested aggregated statistics. + * + * @throws JSONException + */ + @RequestMapping(value = "/layersExtraction.csv", method = RequestMethod.POST, produces = "application/csv; charset=utf-8") + @ResponseBody + public String layersExtractionCsv(@RequestBody String payload, HttpServletResponse response) + throws JSONException, SQLException { + return this.generateStats(payload, REQUEST_TYPE.EXTRACTION, response, FORMAT.CSV); + } + + /** + * This method generates stats for layer usage or extraction and return results + * in CSV or JSON format + * + * @param payload JSON payload, should contain 'startDate', 'endDate', 'limit', + * 'role' + * @param type either layer usage 'USAGE' or layer extraction 'EXTRACTION' + * @param response response + * @param format + * @return + * @throws JSONException + */ + private String generateStats(String payload, REQUEST_TYPE type, HttpServletResponse response, FORMAT format) + throws JSONException, SQLException { + + JSONObject input; + String userId, roleId; + String limit; + Map sqlValues = new HashMap<>(); + + try { + input = new JSONObject(payload); + sqlValues.put("startDate", this.getStartDate(input)); + sqlValues.put("endDate", this.getEndDate(input)); + limit = this.getLimit(input); + userId = this.getUser(input); + roleId = this.getRole(input); + sqlValues.put("role", roleId); + sqlValues.put("user", userId); + sqlValues.put("limit", limit); + + if (sqlValues.get("startDate") == null || sqlValues.get("endDate") == null) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + } catch (Throwable e) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + String sql; + + if (type == REQUEST_TYPE.USAGE) { + sql = "SELECT layer, COUNT(*) AS count " + "FROM ogcstatistics.ogc_services_log " + + "WHERE date >= CAST({startDate} AS timestamp without time zone) AND date < CAST({endDate} AS timestamp without time zone) " + + "AND layer != '' "; + } else if (type == REQUEST_TYPE.EXTRACTION) { + sql = "SELECT layer_name AS layer, COUNT(*) AS count " + "FROM extractorapp.extractor_layer_log " + + "LEFT JOIN extractorapp.extractor_log " + + " ON (extractorapp.extractor_log.id = extractorapp.extractor_layer_log.extractor_log_id) " + + "WHERE creation_date >= CAST({startDate} AS timestamp without time zone) AND creation_date < CAST({endDate} AS timestamp without time zone) " + + "AND is_successful "; + } else { + throw new IllegalArgumentException("Invalid request type : " + type); + } + + if (roleId != null) + sql += " AND {role} = ANY(roles) "; + if (userId != null) { + if (type == REQUEST_TYPE.USAGE) { + sql += " AND user_name = {user} "; + } else if (type == REQUEST_TYPE.EXTRACTION) { + sql += " AND username = {user} "; + } else { + throw new IllegalArgumentException("Invalid request type : " + type); + } + } + + if (type == REQUEST_TYPE.USAGE) { + sql += " GROUP BY layer " + " ORDER BY COUNT(*) DESC "; + } else if (type == REQUEST_TYPE.EXTRACTION) { + sql += " GROUP BY layer_name " + " ORDER BY COUNT(*) DESC"; + } else { + throw new IllegalArgumentException("Invalid request type : " + type); + } + + if (limit != null) + sql += " LIMIT {limit}"; + + final String generatedQuery = queryBuilder.generateQuery(sql, sqlValues); + try (Connection c = dataSource.getConnection(); // + Statement st = c.createStatement(); // + ResultSet sqlRes = st.executeQuery(generatedQuery)) { + + switch (format) { + case JSON: + JSONArray results = new JSONArray(); + while (sqlRes.next()) + results.put(new JSONObject().put("layer", sqlRes.getString("layer")).put("count", + sqlRes.getInt("count"))); + return new JSONObject().put("results", results).toString(4); + case CSV: + StringBuilder res = new StringBuilder("layer,count\n"); + while (sqlRes.next()) + res.append(sqlRes.getString("layer") + "," + sqlRes.getInt("count") + "\n"); + return res.toString(); + default: + throw new JSONException("Invalid format " + format); + } + } + } + + /** + * Gets the statistics by distinct users (number of requests between beginDate + * and endDate). + * + * @param payload the JSON object containing the parameters + * @param response the HTTP Servlet Response object, used to set the 40x HTTP + * code in case of errors. + * + * @return A string representing a JSON object with the requested datas. The + * output JSON has the following form: + * + *
+     *    { "results": [
+     *	    {
+     *        "nb_requests": 3895,
+     *        "organization": "geOrchestra",
+     *        "user": "testadmin"
+     *      }, [...]
+     *     ]
+     *    }
+     *         
+ * + * @throws JSONException + */ + @RequestMapping(value = "/distinctUsers", method = RequestMethod.POST) + @ApiMethod(description = "Returns the distinct active users for a given period. A role can be provided in the query " + + "to limit the results to a given role.
" + "Here are 2 valid examples (with and without a role):
" + + "" + "{ role: ADMINISTRATOR, startDate: 2015-01-01, endDate: 2015-12-01 }" + "
" + + "or:
" + "" + "{ startDate: 2015-01-01, endDate: 2015-12-01 }" + "") + public void distinctUsers(@RequestBody String payload, HttpServletResponse response) throws JSONException, + IOException, InvocationTargetException, SQLException, IllegalAccessException, NoSuchMethodException { + JSONObject input; + String roleId = null; + + response.setContentType("application/json; charset=UTF-8"); + response.setCharacterEncoding("UTF-8"); + + Map sqlValues = new HashMap<>(); + String roleFilter = null; + + // Parse input + try { + input = new JSONObject(payload); + if (!input.has("startDate") || !input.has("endDate")) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + sqlValues.put("startDate", this.convertLocalDateToUTC(input.getString("startDate"))); + sqlValues.put("endDate", this.convertLocalDateToUTC(input.getString("endDate"))); + + if (input.has("role")) { + roleFilter = "ROLE_" + input.getString("role"); + sqlValues.put("role", roleFilter); + } + } catch (Throwable e) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + // construct SQL query + String sql = "SELECT user_name, org, COUNT(*) AS count " + "FROM ogcstatistics.ogc_services_log " + + "WHERE date >= CAST({startDate} AS timestamp without time zone) AND date < CAST({endDate} AS timestamp without time zone) "; + + if (roleFilter != null) + sql += " AND {role} = ANY (roles) "; + + sql += "GROUP BY user_name, org " + "ORDER BY COUNT(*) DESC"; + + // Fetch and format results + final String generatedQuery = queryBuilder.generateQuery(sql, sqlValues); + try (Connection c = dataSource.getConnection(); // + Statement st = c.createStatement(); // + ResultSet res = st.executeQuery(generatedQuery)) { + JSONArray results = new JSONArray(); + while (res.next()) { + if (this.excludedUsers.contains(res.getString("user_name"))) + continue; + JSONObject row = new JSONObject(); + row.put("user", res.getString("user_name")); + row.put("organization", res.getString("org")); + row.put("nb_requests", res.getInt("count")); + results.put(row); + } + String jsonOutput = new JSONObject().put("results", results).toString(4); + + PrintWriter writer = response.getWriter(); + writer.print(jsonOutput); + writer.close(); + } + } + + /** + * Calculates the appropriate granularity given the begin date and the end date. + * + * @param beginDate the begin date. + * @param endDate the end date. + * @return the most relevant GRANULARITY. + */ + private GRANULARITY guessGranularity(String beginDate, String endDate) { + DateTime from = DateTime.parse(beginDate, this.dbOutputFormatter); + DateTime to = DateTime.parse(endDate, this.dbOutputFormatter); + + Duration duration = new Duration(from, to); + long numdays = duration.getStandardDays(); + if (numdays < 2) { + return GRANULARITY.HOUR; + } else if (numdays < 90) { + return GRANULARITY.DAY; + } else if (numdays < 365) { + return GRANULARITY.WEEK; + } else { + return GRANULARITY.MONTH; + } + } + + /** + * Convert Date (with time) from configured local timezone to UTC. This method + * is used to convert date sent by UI to date with same timezone as database + * records. Ex : "2016-11-15" will be convert to "2016-11-14 23:00:00" if your + * local timezone is Europe/Paris (+01:00) + * + * @param rawDate Date to convert, should looks like : 2016-02-12 + * @return String representation of datatime convert to UTC timezone with + * following format : 2016-11-14 23:00:00 + * @throws ParseException if input date is not parsable + */ + + private String convertLocalDateToUTC(String rawDate) { + DateTime localDatetime = this.localInputFormatter.parseDateTime(rawDate); + return this.dbOutputFormatter.print(localDatetime.toInstant()); + } + + /** + * Convert date from UTC to local configured timezone. This method is used to + * convert dates returns by database. + * + * @param rawDate raw date from database with format : "2016-02-12 23" or + * "2016-02-12" or "2016-06" or "2016-02" + * @return date in local timezone with hour + * @throws ParseException if input date is not parsable + */ + private String convertUTCDateToLocal(String rawDate, GRANULARITY granularity) throws ParseException { + DateTimeFormatter inputFormatter = null; + DateTimeFormatter outputFormatter = null; + switch (granularity) { + case HOUR: + inputFormatter = this.dbHourInputFormatter; + outputFormatter = this.dbHourOutputFormatter; + break; + case DAY: + inputFormatter = this.dbDayInputFormatter; + outputFormatter = this.dbDayOutputFormatter; + break; + case WEEK: + inputFormatter = this.dbWeekInputFormatter; + outputFormatter = this.dbWeekOutputFormatter; + break; + case MONTH: + inputFormatter = this.dbMonthInputFormatter; + outputFormatter = this.dbMonthOutputFormatter; + break; + } + DateTime localDatetime = inputFormatter.parseDateTime(rawDate); + return outputFormatter.print(localDatetime.toInstant()); + } + + private String getRole(JSONObject payload) throws JSONException { + if (payload.has("role")) { + return "ROLE_" + payload.getString("role"); + } + return null; + } + + private String getUser(JSONObject payload) throws JSONException { + if (payload.has("user")) + return payload.getString("user"); + else + return null; + } + + private String getLimit(JSONObject payload) throws JSONException { + if (payload.has("limit")) + return String.valueOf(payload.getInt("limit")); + else + return null; + } + + private String getDateField(JSONObject payload, String field) throws JSONException, ParseException { + if (payload.has(field)) + return this.convertLocalDateToUTC(payload.getString(field)); + else + return null; + } + + private String getStartDate(JSONObject payload) throws JSONException, ParseException { + return this.getDateField(payload, "startDate"); + } + + private String getEndDate(JSONObject payload) throws JSONException, ParseException { + return this.getDateField(payload, "endDate"); + } +} diff --git a/analytics/src/main/java/org/georchestra/analytics/Utf8ResourceBundle.java b/analytics/src/main/java/org/georchestra/analytics/Utf8ResourceBundle.java index 463293ba11..ea13d3e727 100644 --- a/analytics/src/main/java/org/georchestra/analytics/Utf8ResourceBundle.java +++ b/analytics/src/main/java/org/georchestra/analytics/Utf8ResourceBundle.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + package org.georchestra.analytics; import java.io.UnsupportedEncodingException; @@ -38,14 +57,18 @@ private static class Utf8PropertyResourceBundle extends ResourceBundle { private Utf8PropertyResourceBundle(PropertyResourceBundle bundle) { this.bundle = bundle; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see java.util.ResourceBundle#getKeys() */ public Enumeration getKeys() { return bundle.getKeys(); } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see java.util.ResourceBundle#handleGetObject(java.lang.String) */ diff --git a/analytics/src/main/java/org/georchestra/analytics/model/AbstractModel.java b/analytics/src/main/java/org/georchestra/analytics/model/AbstractModel.java index 6e5fa11159..f897c39f99 100644 --- a/analytics/src/main/java/org/georchestra/analytics/model/AbstractModel.java +++ b/analytics/src/main/java/org/georchestra/analytics/model/AbstractModel.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + package org.georchestra.analytics.model; import java.sql.Connection; @@ -6,46 +25,44 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +//import java.util.logging.Logger; + +import javax.sql.DataSource; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; public class AbstractModel { - protected PostGresqlConnection postgresqlConnection; - - private final String countQ = "SELECT count(*) from (@query@) as res;"; - - public AbstractModel(PostGresqlConnection pgpool) { - postgresqlConnection = pgpool; - } + @Autowired + protected DataSource dataSource; - /** - * Prepares the statement with controller attributes - * - * @return - * @throws SQLException - */ - protected PreparedStatement prepareStatement(Connection con, final String query, - final int month, final int year, final int start, final int limit, final String sort, - List extraFilters) throws SQLException { + private final String countQ = "SELECT count(*) from (@query@) as res;"; - String q = query.replace("@sort@", sort); - PreparedStatement st = con.prepareStatement(q); + /** + * Prepares the statement with controller attributes + * + * @return + * @throws SQLException + */ + protected PreparedStatement prepareStatement(Connection con, final String query, final int month, final int year, + final int start, final int limit, final String sort, List extraFilters) throws SQLException { - // Extra filters come first (replacing the WHERE clause by WHERE ...) - int curParam = 1; - for (String extrafilter : extraFilters) { - st.setString(curParam++, extrafilter); - } + String q = query.replace("@sort@", sort); + PreparedStatement st = con.prepareStatement(q); + // Extra filters come first (replacing the WHERE clause by WHERE ...) + int curParam = 1; + for (String extrafilter : extraFilters) { + st.setString(curParam++, extrafilter); + } if ((month > 0) && (year > 0)) { st.setString(curParam++, String.format("%4d-%02d-01 00:00", year, month)); if (month < 12) - st.setString(curParam++, - String.format("%4d-%02d-01 00:00", year, month + 1)); + st.setString(curParam++, String.format("%4d-%02d-01 00:00", year, month + 1)); else st.setString(curParam++, String.format("%4d-01-01 00:00", year + 1)); } else { @@ -56,37 +73,32 @@ protected PreparedStatement prepareStatement(Connection con, final String query, st.setInt(curParam++, limit); st.setInt(curParam++, start); - return st; - } - - /** - * Counts all the results of the given query. - * - * Builds the count query from the filter query, removing LIMIT and OFFSET keywords - * and includes in a count query named "countQ". - * - * @return number of results - * @throws SQLException - */ - protected int getCount(Connection con, final String query, - final int month, final int year, final String sort, List extraFilters) throws SQLException { - - ResultSet rs = null; - PreparedStatement st = null; - int count = 0; - String q = query.replace("@sort@", sort); - q = q.replace("LIMIT ? OFFSET ?;", ""); - q = countQ.replace("@query@", q); - - - - try { - st = con.prepareStatement(q); - - int curParam = 1; - for (String extrafilter : extraFilters) { - st.setString(curParam++, extrafilter); - } + return st; + } + + /** + * Counts all the results of the given query. + * + * Builds the count query from the filter query, removing LIMIT and OFFSET + * keywords and includes in a count query named "countQ". + * + * @return number of results + * @throws SQLException + */ + protected int getCount(Connection con, final String query, final int month, final int year, final String sort, + List extraFilters) throws SQLException { + + int count = 0; + String q = query.replace("@sort@", sort); + q = q.replace("LIMIT ? OFFSET ?;", ""); + q = countQ.replace("@query@", q); + + try (PreparedStatement st = con.prepareStatement(q)) { + + int curParam = 1; + for (String extrafilter : extraFilters) { + st.setString(curParam++, extrafilter); + } if ((month > 0) && (year > 0)) { st.setString(curParam++, String.format("%4d-%02d-01 00:00", year, month)); @@ -101,98 +113,76 @@ protected int getCount(Connection con, final String query, st.setString(curParam++, "1970-01-01 00:00"); st.setString(curParam++, "2032-01-01 00:00"); } - rs = st.executeQuery(); - if(rs.next()) { - count = rs.getInt(1); - } - } catch(SQLException e) { - throw e; - } finally { - if (st != null) st.close(); - if (rs != null) rs.close(); - } - return count; - } - - /** - * Generic statistics data access. Gets all statistics of a type, filtered by date, ordered and - * sampled (offset, limit). The ResultSet is parsed and all data are inserted in a JSON object - * actually returned. - * @param filter - * @return JSON object containing all results - * @throws SQLException - * @throws JSONException - */ - public JSONObject getStats(final int month, final int year, final int start, final int limit, - final String sort, String filter, final String query, StrategyModel strategy) throws SQLException, JSONException { - - JSONObject object = new JSONObject(); - ResultSet rs = null; - Connection con = null; - PreparedStatement st = null; - - // The current block code corresponds to the deprecated - // addFilters() method - String q = new String(query); - List extraFilters = new ArrayList(); - - if ((filter != null) && (! "".equals(filter))) { - - JSONArray arr = new JSONArray(filter); - - StringBuilder sb = new StringBuilder(); - sb.append("WHERE"); - - for (int i=0; i < arr.length() ; ++i) { - JSONObject f = arr.getJSONObject(i); - sb.append(" "); - sb.append(f.getString("property")); - // TODO we should avoid casting if we can predict the type - sb.append("::text = ? "); - extraFilters.add(f.getString("value")); - sb.append(" AND"); - } - sb.append(" "); - - // Case-sensivity of the where - q = q.replace("WHERE", sb.toString()); - } - // end block + try (ResultSet rs = st.executeQuery()) { + if (rs.next()) { + count = rs.getInt(1); + } + } + } + return count; + } + + /** + * Generic statistics data access. Gets all statistics of a type, filtered by + * date, ordered and sampled (offset, limit). The ResultSet is parsed and all + * data are inserted in a JSON object actually returned. + * + * @param filter + * @return JSON object containing all results + * @throws SQLException + * @throws JSONException + */ + public JSONObject getStats(final int month, final int year, final int start, final int limit, final String sort, + String filter, final String query, StrategyModel strategy) throws SQLException, JSONException { + + JSONObject object = new JSONObject(); + + // The current block code corresponds to the deprecated + // addFilters() method + List extraFilters = new ArrayList(); + String q = query; + if ((filter != null) && (!"".equals(filter))) { + + JSONArray arr = new JSONArray(filter); + + StringBuilder sb = new StringBuilder(); + sb.append("WHERE"); + + for (int i = 0; i < arr.length(); ++i) { + JSONObject f = arr.getJSONObject(i); + sb.append(" "); + sb.append(f.getString("property")); + // TODO we should avoid casting if we can predict the type + sb.append("::text = ? "); + extraFilters.add(f.getString("value")); + sb.append(" AND"); + } + sb.append(" "); - try { - //String q = addFilters(query, filter); - - con = postgresqlConnection.getConnection(); - int count = getCount(con, q, month, year, sort, extraFilters); - st = prepareStatement(con, q, month, year, start, limit, sort, extraFilters); - rs = st.executeQuery(); - - JSONArray jsarr = strategy.process(rs); - object.put("success", true); - object.put("results", jsarr); - object.put("total", count); - - return object; - - } catch (SQLException e) { - throw e; + // Case-sensivity of the where + q = query.replace("WHERE", sb.toString()); + } + // end block - } catch (JSONException e) { - throw e; + try (Connection con = dataSource.getConnection()) { + int count = getCount(con, q, month, year, sort, extraFilters); + try (PreparedStatement st = prepareStatement(con, q, month, year, start, limit, sort, extraFilters)) { + try (ResultSet rs = st.executeQuery()) { - } finally { - if (st != null) st.close(); - if (rs != null) rs.close(); + JSONArray jsarr = strategy.process(rs); + object.put("success", true); + object.put("results", jsarr); + object.put("total", count); - if (con != null) { - con.close(); - } - } - } + return object; + } + } + } + } - protected abstract class StrategyModel { + protected abstract class StrategyModel { - protected abstract JSONArray process(ResultSet rs) throws SQLException, JSONException; - } + protected abstract JSONArray process(ResultSet rs) throws SQLException, JSONException; + } } \ No newline at end of file diff --git a/analytics/src/main/java/org/georchestra/analytics/model/ExtractorStatsModel.java b/analytics/src/main/java/org/georchestra/analytics/model/ExtractorStatsModel.java deleted file mode 100644 index c207873b84..0000000000 --- a/analytics/src/main/java/org/georchestra/analytics/model/ExtractorStatsModel.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.georchestra.analytics.model; - -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -public class ExtractorStatsModel extends AbstractModel { - - public ExtractorStatsModel(PostGresqlConnection pgpool) { - super(pgpool); - } - - private final String selectLayersQ = "SELECT " - + " y.ows_url " - + " , y.ows_type " - + " , y.layer_name " - + " , COUNT(*) AS count " - + "FROM " - + " downloadform.extractorapp_log l " - + " , downloadform.extractorapp_layers y " - + "WHERE " - + " requested_at >= ?::timestamp " - + "AND " - + " requested_at < ?::timestamp " - + "AND " - + " l.id = y.extractorapp_log_id " - + "GROUP BY " - + " y.layer_name " - + " , y.ows_url " - + " , y.ows_type " - + "ORDER BY " - + " @sort@ " - + "LIMIT ? OFFSET ?;"; - - private final String selectUsersQ = "SELECT " - + " l.username AS username " - + " , COUNT(*) AS count " - + "FROM " - + " downloadform.extractorapp_log l " - + " , downloadform.extractorapp_layers y " - + "WHERE " - + " requested_at >= ?::timestamp " - + "AND " - + " requested_at < ?::timestamp " - + "AND " - + " l.id = y.extractorapp_log_id " - + "GROUP BY " - + " l.username " - + "ORDER BY " - + " @sort@ " - + "LIMIT ? OFFSET ?;"; - - private final String selectGroupsQ = "SELECT " - + " l.company AS company " - + " , count(*) AS count " - + "FROM " - + " downloadform.extractorapp_log l " - + " , downloadform.extractorapp_layers y " - + "WHERE " - + " requested_at >= ?::timestamp " - + "AND " - + " requested_at < ?::timestamp " - + "AND " - + " l.id = y.extractorapp_log_id " - + "GROUP BY " - + " l.company " - + "ORDER BY " - + " @sort@ " - + "LIMIT ? OFFSET ?;"; - - public JSONObject getLayersStats(final int month, final int year, final int start, final int limit, final String sort, final String filter) throws SQLException, JSONException { - - return getStats(month, year, start, limit, sort, filter, selectLayersQ, new StrategyModel() { - - protected JSONArray process(ResultSet rs) throws SQLException, JSONException { - JSONArray jsarr = new JSONArray(); - while (rs.next()) { - JSONObject res = new JSONObject(); - res.put("ows_type", rs.getString("ows_type")); - res.put("ows_url", rs.getString("ows_url")); - res.put("layer_name", rs.getString("layer_name")); - res.put("count", rs.getInt("count")); - jsarr.put(res); - } - return jsarr; - } - }); - } - - public JSONObject getUsersStats(final int month, final int year, final int start, final int limit, final String sort, final String filter) throws SQLException, JSONException { - - return getStats(month, year, start, limit, sort, filter, selectUsersQ, new StrategyModel() { - - protected JSONArray process(ResultSet rs) throws SQLException, JSONException { - JSONArray jsarr = new JSONArray(); - while (rs.next()) { - JSONObject res = new JSONObject(); - res.put("username", rs.getString("username")); - res.put("count", rs.getInt("count")); - jsarr.put(res); - } - return jsarr; - } - }); - } - - public JSONObject getGroupsStats(final int month, final int year, final int start, final int limit, final String sort, final String filter) throws SQLException, JSONException { - - return getStats(month, year, start, limit, sort, filter, selectGroupsQ, new StrategyModel() { - - protected JSONArray process(ResultSet rs) throws SQLException, JSONException { - JSONArray jsarr = new JSONArray(); - while (rs.next()) { - JSONObject res = new JSONObject(); - res.put("company", rs.getString("company")); - res.put("count", rs.getInt("count")); - jsarr.put(res); - } - return jsarr; - } - }); - } -} diff --git a/analytics/src/main/java/org/georchestra/analytics/model/GeonetworkStatsModel.java b/analytics/src/main/java/org/georchestra/analytics/model/GeonetworkStatsModel.java deleted file mode 100644 index 5aa1b1c014..0000000000 --- a/analytics/src/main/java/org/georchestra/analytics/model/GeonetworkStatsModel.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.georchestra.analytics.model; - -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -public class GeonetworkStatsModel extends AbstractModel { - - - public GeonetworkStatsModel(PostGresqlConnection pgpool) { - super(pgpool); - } - - private final String selectFilesQ = "SELECT " - + " filename " - + " , metadata_id " - + " , COUNT(*) AS count " - + "FROM " - + " downloadform.geonetwork_log " - + "WHERE " - + " requested_at >= ?::timestamp " - + "AND " - + " requested_at < ?::timestamp " - + "GROUP BY " - + " metadata_id " - + " , filename " - + "ORDER BY " - + " @sort@ " - + "LIMIT ? OFFSET ?;"; - - private final String selectUsersQ = "SELECT " - + " username " - + " , COUNT(*) AS count " - + "FROM " - + " downloadform.geonetwork_log " - + "WHERE " - + " requested_at >= ?::timestamp " - + "AND " - + " requested_at < ?::timestamp " - + "GROUP BY " - + " username " - + "ORDER BY " - + " @sort@ " - + "LIMIT ? OFFSET ?;"; - - private final String selectGroupsQ = "SELECT " - + " company" - + " , COUNT(*) AS count " - + "FROM " - + " downloadform.geonetwork_log " - + "WHERE " - + " requested_at >= ?::timestamp " - + "AND " - + " requested_at < ?::timestamp " - + "GROUP BY " - + " company " - + "ORDER BY " - + " @sort@ " - + "LIMIT ? OFFSET ?;"; - - public JSONObject getFilesStats(final int month, final int year, final int start, final int limit, final String sort, final String filter) throws SQLException, JSONException { - - return getStats(month, year, start, limit, sort, filter, selectFilesQ, new StrategyModel() { - - protected JSONArray process(ResultSet rs) throws SQLException, JSONException { - JSONArray jsarr = new JSONArray(); - while (rs.next()) { - JSONObject res = new JSONObject(); - res.put("filename", rs.getString("filename")); - res.put("metadata_id", rs.getString("metadata_id")); - res.put("count", rs.getInt("count")); - jsarr.put(res); - } - return jsarr; - } - }); - } - - public JSONObject getUsersStats(final int month, final int year, final int start, final int limit, final String sort, final String filter) throws SQLException, JSONException { - - return getStats(month, year, start, limit, sort, filter, selectUsersQ, new StrategyModel() { - - protected JSONArray process(ResultSet rs) throws SQLException, JSONException { - JSONArray jsarr = new JSONArray(); - while (rs.next()) { - JSONObject res = new JSONObject(); - res.put("username", rs.getString("username")); - res.put("count", rs.getInt("count")); - jsarr.put(res); - } - return jsarr; - } - }); - } - - public JSONObject getGroupsStats(final int month, final int year, final int start, final int limit, final String sort, final String filter) throws SQLException, JSONException { - - return getStats(month, year, start, limit, sort, filter, selectGroupsQ, new StrategyModel() { - - protected JSONArray process(ResultSet rs) throws SQLException, JSONException { - JSONArray jsarr = new JSONArray(); - while (rs.next()) { - JSONObject res = new JSONObject(); - res.put("company", rs.getString("company")); - res.put("count", rs.getInt("count")); - jsarr.put(res); - } - return jsarr; - } - }); - } -} diff --git a/analytics/src/main/java/org/georchestra/analytics/model/OGCStatsModel.java b/analytics/src/main/java/org/georchestra/analytics/model/OGCStatsModel.java index fa06e70f84..9545ecf40d 100644 --- a/analytics/src/main/java/org/georchestra/analytics/model/OGCStatsModel.java +++ b/analytics/src/main/java/org/georchestra/analytics/model/OGCStatsModel.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + package org.georchestra.analytics.model; import java.sql.ResultSet; @@ -7,109 +26,73 @@ import org.json.JSONException; import org.json.JSONObject; -public class OGCStatsModel extends AbstractModel { - - public OGCStatsModel(PostGresqlConnection pgpool) { - super(pgpool); - } +public class OGCStatsModel extends AbstractModel { - private final String selectLayersQ = "SELECT " - + " service" - + " , layer" - + " , request" - + " , COUNT(*) AS count " - + "FROM " - + " ogcstatistics.ogc_services_log " - + "WHERE " - + " date >= ?::timestamp " - + "AND " - + " date < ?::timestamp " - + "GROUP BY " - + " layer" - + " , service" - + " , request " - + "ORDER BY " - + " @sort@ " - + "LIMIT ? OFFSET ?;"; + private final String selectLayersQ = "SELECT " + " service" + " , layer" + " , request" + + " , COUNT(*) AS count " + "FROM " + " ogcstatistics.ogc_services_log " + "WHERE " + + " date >= ?::timestamp " + "AND " + " date < ?::timestamp " + "GROUP BY " + " layer" + + " , service" + " , request " + "ORDER BY " + " @sort@ " + "LIMIT ? OFFSET ?;"; - private final String selectUsersQ = "SELECT " - + " user_name " - + " , COUNT(*) AS count " - + "FROM " - + " ogcstatistics.ogc_services_log " - +"WHERE " - + " date >= ?::timestamp " - + "AND " - + " date < ?::timestamp " - + "GROUP BY " - + " user_name " - + "ORDER BY " - + " @sort@ " - + "LIMIT ? OFFSET ?;"; + private final String selectUsersQ = "SELECT " + " user_name " + " , COUNT(*) AS count " + "FROM " + + " ogcstatistics.ogc_services_log " + "WHERE " + " date >= ?::timestamp " + "AND " + + " date < ?::timestamp " + "GROUP BY " + " user_name " + "ORDER BY " + " @sort@ " + + "LIMIT ? OFFSET ?;"; - private final String selectGroupsQ = "SELECT " - + " org" - + " , COUNT(*) AS count " - + "FROM " - + " ogcstatistics.ogc_services_log " - + "WHERE " - + " date >= ?::timestamp " - + "AND " - + " date < ?::timestamp " - + "GROUP BY " - + " org " - + "ORDER BY " - + " @sort@ " - + "LIMIT ? OFFSET ?;"; + private final String selectOrgsQ = "SELECT " + " org" + " , COUNT(*) AS count " + "FROM " + + " ogcstatistics.ogc_services_log " + "WHERE " + " date >= ?::timestamp " + "AND " + + " date < ?::timestamp " + "GROUP BY " + " org " + "ORDER BY " + " @sort@ " + "LIMIT ? OFFSET ?;"; - public JSONObject getLayersStats(final int month, final int year, final int start, final int limit, final String sort, final String filter) throws SQLException, JSONException { + public JSONObject getLayersStats(final int month, final int year, final int start, final int limit, + final String sort, final String filter) throws SQLException, JSONException { - return getStats(month, year, start, limit, sort, filter, selectLayersQ, new StrategyModel() { + return getStats(month, year, start, limit, sort, filter, selectLayersQ, new StrategyModel() { - protected JSONArray process(ResultSet rs) throws SQLException, JSONException { - JSONArray jsarr = new JSONArray(); - while (rs.next()) { - JSONObject res = new JSONObject(); - res.put("service", rs.getString("service")); - res.put("layer", rs.getString("layer")); - res.put("request", rs.getString("request")); - res.put("count", rs.getInt("count")); - jsarr.put(res); - } - return jsarr; - } - }); - } + protected JSONArray process(ResultSet rs) throws SQLException, JSONException { + JSONArray jsarr = new JSONArray(); + while (rs.next()) { + JSONObject res = new JSONObject(); + res.put("service", rs.getString("service")); + res.put("layer", rs.getString("layer")); + res.put("request", rs.getString("request")); + res.put("count", rs.getInt("count")); + jsarr.put(res); + } + return jsarr; + } + }); + } - public JSONObject getUsersStats(final int month, final int year, final int start, final int limit, final String sort, final String filter) throws SQLException, JSONException { + public JSONObject getUsersStats(final int month, final int year, final int start, final int limit, + final String sort, final String filter) throws SQLException, JSONException { - return getStats(month, year, start, limit, sort, filter, selectUsersQ, new StrategyModel() { - protected JSONArray process(ResultSet rs) throws SQLException, JSONException { - JSONArray jsarr = new JSONArray(); - while (rs.next()) { - JSONObject res = new JSONObject(); - res.put("user_name", rs.getString("user_name")); - res.put("count", rs.getInt("count")); - jsarr.put(res); - } - return jsarr; - } - }); - } + return getStats(month, year, start, limit, sort, filter, selectUsersQ, new StrategyModel() { + protected JSONArray process(ResultSet rs) throws SQLException, JSONException { + JSONArray jsarr = new JSONArray(); + while (rs.next()) { + JSONObject res = new JSONObject(); + res.put("user_name", rs.getString("user_name")); + res.put("count", rs.getInt("count")); + jsarr.put(res); + } + return jsarr; + } + }); + } - public JSONObject getGroupsStats(final int month, final int year, final int start, final int limit, final String sort, final String filter) throws SQLException, JSONException { + public JSONObject getOrgsStats(final int month, final int year, final int start, final int limit, final String sort, + final String filter) throws SQLException, JSONException { - return getStats(month, year, start, limit, sort, filter, selectGroupsQ, new StrategyModel() { - protected JSONArray process(ResultSet rs) throws SQLException, JSONException { - JSONArray jsarr = new JSONArray(); - while (rs.next()) { - JSONObject res = new JSONObject(); - res.put("org", rs.getString("org")); - res.put("count", rs.getInt("count")); - jsarr.put(res); - } - return jsarr; - } - }); - } + return getStats(month, year, start, limit, sort, filter, selectOrgsQ, new StrategyModel() { + protected JSONArray process(ResultSet rs) throws SQLException, JSONException { + JSONArray jsarr = new JSONArray(); + while (rs.next()) { + JSONObject res = new JSONObject(); + res.put("org", rs.getString("org")); + res.put("count", rs.getInt("count")); + jsarr.put(res); + } + return jsarr; + } + }); + } } diff --git a/analytics/src/main/java/org/georchestra/analytics/model/PostGresqlConnection.java b/analytics/src/main/java/org/georchestra/analytics/model/PostGresqlConnection.java deleted file mode 100644 index 0556cf7edc..0000000000 --- a/analytics/src/main/java/org/georchestra/analytics/model/PostGresqlConnection.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.georchestra.analytics.model; - -import java.sql.Connection; -import java.sql.SQLException; - -import org.apache.commons.dbcp.BasicDataSource; - -public class PostGresqlConnection { - - private BasicDataSource basicDataSource; - - public PostGresqlConnection (String jdbcUrl) { - - basicDataSource = new BasicDataSource(); - - basicDataSource.setDriverClassName("org.postgresql.Driver"); - - basicDataSource.setTestOnBorrow(true); - - basicDataSource.setPoolPreparedStatements(true); - basicDataSource.setMaxOpenPreparedStatements(-1); - - basicDataSource.setDefaultReadOnly(false); - basicDataSource.setDefaultAutoCommit(false); - - basicDataSource.setUrl(jdbcUrl); - } - - public Connection getConnection() throws SQLException - { - return basicDataSource.getConnection(); - } - -} - diff --git a/analytics/src/main/java/org/georchestra/analytics/util/CSVUtil.java b/analytics/src/main/java/org/georchestra/analytics/util/CSVUtil.java index 4febc57f7e..3c91bc4c08 100644 --- a/analytics/src/main/java/org/georchestra/analytics/util/CSVUtil.java +++ b/analytics/src/main/java/org/georchestra/analytics/util/CSVUtil.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + package org.georchestra.analytics.util; import java.util.Iterator; @@ -8,41 +27,41 @@ /** * Static Class providing utilities concerning CSV format + * * @author fgravin * */ public class CSVUtil { - public static final String CSV_SEP = ";"; + public static final String CSV_SEP = ";"; public static final String RES_FIELD = "results"; public static final String CSV_EXT = ".csv"; - + /** * Format a JSON Object to a CSV String. All sub objects of the field * "RES_FIELD" are parsed and format to the CSV * * @param obj - * @return CSV String + * @return CSV String * @throws JSONException */ public static final String JSONToCSV(JSONObject obj) throws JSONException { - StringBuilder sBuilder = new StringBuilder(); - JSONArray jArr = obj.getJSONArray(RES_FIELD); - - for(int i=0;i it = curObj.keys(); - - while(it.hasNext()) { - String key = it.next(); - sBuilder.append(curObj.getString(key)); - sBuilder.append(CSV_SEP); - } - sBuilder.append("\r\n"); - } - return sBuilder.toString(); + StringBuilder sBuilder = new StringBuilder(); + JSONArray jArr = obj.getJSONArray(RES_FIELD); + + for (int i = 0; i < jArr.length(); ++i) { + JSONObject curObj = jArr.getJSONObject(i); + + @SuppressWarnings("unchecked") + Iterator it = curObj.keys(); + + while (it.hasNext()) { + String key = it.next(); + sBuilder.append(curObj.get(key).toString()); + sBuilder.append(CSV_SEP); + } + sBuilder.append("\r\n"); + } + return sBuilder.toString(); } } - diff --git a/analytics/src/main/java/org/georchestra/analytics/util/QueryBuilder.java b/analytics/src/main/java/org/georchestra/analytics/util/QueryBuilder.java new file mode 100644 index 0000000000..3dfdf8e8ff --- /dev/null +++ b/analytics/src/main/java/org/georchestra/analytics/util/QueryBuilder.java @@ -0,0 +1,59 @@ +package org.georchestra.analytics.util; + +import java.sql.SQLException; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * The purpose of this class is to keep parameter replacement feature without + * using SQL prepared statements. + * + * Prepared statements implies that SQL plan execution is computed before + * parameters values are known. So with partitioned table, execution plan is + * always wrong and all tables are scanned. This lead to very long query. To fix + * this issue, this class, use a standard Statement instance to use parameter + * replacement and then get raw SQL query with toString() method. Then a second + * method provide a reconnection feature and test db connection before sending + * real query to the database. If test fails, one reconnection is tried. Another + * feature of this class, it adds a name on sql parameter: '?' is replaced by + * parameter name in braces : {\w+} + * + * Example : + * + *
+ * 
+ * String sql = "SELECT * FROM users WHERE group_name = {group} LIMIT {count}";
+ * Map sqlValues = new HashMap();
+ * sqlValues.put("group", "admin"); sqlValues.put("count", 100);
+ *
+ * QueryBuilder builder = new QueryBuilder(); 
+ * String rawSql = builder.generateQuery(sql, sqlValues);
+ * Statement st = ... 
+ * ResultSet res = st.executeQuery(rawSql);
+ * 
+ * 
+ */ +public class QueryBuilder { + + private static final Pattern namedParameterPattern = Pattern.compile("\\{(\\w+)\\}"); + + public String generateQuery(String sql, Map values) throws SQLException { + // Replace named parameter with standard prepared statement parameter : '?' + Matcher m = namedParameterPattern.matcher(sql); + + while (m.find()) { + String parameterName = m.group(1); + if (!values.containsKey(parameterName)) { + throw new IllegalArgumentException( + "No value specified for parameter : " + parameterName + " in " + sql); + } + String parameterValue = values.get(parameterName); + String value = null == parameterValue ? "null" : String.format("'%s'", parameterValue); + sql = sql.replaceFirst("\\{" + parameterName + "\\}", value); + m = namedParameterPattern.matcher(sql); + } + + return sql; + } +} diff --git a/analytics/src/main/resources/analytics/i18n/index_fr.properties b/analytics/src/main/resources/analytics/i18n/index_fr.properties index 072b36fc07..a84b46e6ae 100644 --- a/analytics/src/main/resources/analytics/i18n/index_fr.properties +++ b/analytics/src/main/resources/analytics/i18n/index_fr.properties @@ -3,6 +3,6 @@ # index.jsp title.analytics=Statistiques loading=Chargement... -need.javascript:Cette application nécessite le support de JavaScript par \ +need.javascript=Cette application nécessite le support de JavaScript par \ votre navigateur. Merci de l'activer. diff --git a/analytics/src/main/resources/analytics/i18n/index_nl.properties b/analytics/src/main/resources/analytics/i18n/index_nl.properties new file mode 100644 index 0000000000..abf9e060d5 --- /dev/null +++ b/analytics/src/main/resources/analytics/i18n/index_nl.properties @@ -0,0 +1,8 @@ +### Translation file + +# index.jsp +title.analytics=Statistieken +loading=Bezig met laden... +need.javascript=deze applicatie vereist JavaScript-ondersteuning via \ +uw browser. Activeer het alstublieft. + diff --git a/analytics/src/main/webapp/WEB-INF/classes/.gitignore b/analytics/src/main/webapp/WEB-INF/classes/.gitignore deleted file mode 100644 index c96a04f008..0000000000 --- a/analytics/src/main/webapp/WEB-INF/classes/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file diff --git a/analytics/src/main/webapp/WEB-INF/classes/persistence.xml b/analytics/src/main/webapp/WEB-INF/classes/persistence.xml new file mode 100644 index 0000000000..d9735775bd --- /dev/null +++ b/analytics/src/main/webapp/WEB-INF/classes/persistence.xml @@ -0,0 +1,8 @@ + + + + org.hibernate.jpa.HibernatePersistenceProvider + + diff --git a/analytics/src/main/webapp/WEB-INF/jsp/header.jsp b/analytics/src/main/webapp/WEB-INF/jsp/header.jsp new file mode 100644 index 0000000000..c480688aa2 --- /dev/null +++ b/analytics/src/main/webapp/WEB-INF/jsp/header.jsp @@ -0,0 +1,9 @@ +<%@ page pageEncoding="UTF-8"%> + + + +
+ +
+
+
diff --git a/analytics/src/main/webapp/WEB-INF/jsp/index.jsp b/analytics/src/main/webapp/WEB-INF/jsp/index.jsp index c5ae4411a9..8759144355 100644 --- a/analytics/src/main/webapp/WEB-INF/jsp/index.jsp +++ b/analytics/src/main/webapp/WEB-INF/jsp/index.jsp @@ -1,3 +1,24 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <%@ page language="java" %> @@ -6,46 +27,31 @@ <%@ page contentType="text/html; charset=UTF-8"%> <%@ page pageEncoding="UTF-8"%> <%@ page isELIgnored="false" %> -<%@ page import="org.springframework.web.context.support.WebApplicationContextUtils" %> -<%@ page import="org.springframework.context.ApplicationContext" %> -<%@ page import="org.springframework.web.servlet.support.RequestContextUtils" %> -<%@ page import="org.georchestra.commons.configuration.GeorchestraConfiguration" %> <% -String defaultLanguage = null, defaultInstanceName = null, instanceName = null; - -String defaultConfigJs = "resources/js/app/config.js"; -try { - ApplicationContext ctx = RequestContextUtils.getWebApplicationContext(request); - if (ctx.getBean(GeorchestraConfiguration.class).activated()) { - defaultLanguage = ctx.getBean(GeorchestraConfiguration.class).getProperty("language"); - defaultInstanceName = ctx.getBean(GeorchestraConfiguration.class).getProperty("instance"); - defaultConfigJs = "ws/app/js/GEOR_custom.js"; - } -} catch (Exception e) {} - - -String lang = request.getParameter("lang"); -if (lang == null || (!lang.equals("en") && !lang.equals("es") && !lang.equals("fr") && !lang.equals("de"))) { - if (defaultLanguage == null) { - lang = "en"; - } else { - lang = defaultLanguage; - } -} +Locale rLocale = request.getLocale(); +ResourceBundle bundle = Utf8ResourceBundle.getBundle("analytics.i18n.index", rLocale); + +String detectedLanguage = rLocale.getLanguage(); +String forcedLang = request.getParameter("lang"); + +String lang = (String) request.getAttribute("defaultLanguage"); -if (defaultInstanceName == null) { - instanceName = "geOrchestra"; +if (forcedLang != null) { + if (forcedLang.equals("en") || forcedLang.equals("es") || forcedLang.equals("ru") || forcedLang.equals("fr") || forcedLang.equals("de") || forcedLang.equals("nl")) { + lang = forcedLang; + } } else { - instanceName = defaultInstanceName; + if (detectedLanguage.equals("en") || detectedLanguage.equals("es") || detectedLanguage.equals("ru") || detectedLanguage.equals("fr") || detectedLanguage.equals("de") || detectedLanguage.equals("nl")) { + lang = detectedLanguage; + } } -Locale l = new Locale(lang); -ResourceBundle resource = Utf8ResourceBundle.getBundle("analytics.i18n.index",l); javax.servlet.jsp.jstl.core.Config.set( - request, - javax.servlet.jsp.jstl.core.Config.FMT_LOCALIZATION_CONTEXT, - new javax.servlet.jsp.jstl.fmt.LocalizationContext(resource)); + request, + javax.servlet.jsp.jstl.core.Config.FMT_LOCALIZATION_CONTEXT, + new javax.servlet.jsp.jstl.fmt.LocalizationContext(bundle) +); %> - <fmt:message key="title.analytics"/> - <%= instanceName %> + <fmt:message key="title.analytics"/> - ${instanceName} - - - - - -

Eclipse Public License - v 1.0

- -

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

- -

1. DEFINITIONS

- -

"Contribution" means:

- -

a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

-

b) in the case of each subsequent Contributor:

-

i) changes to the Program, and

-

ii) additions to the Program;

-

where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

- -

"Contributor" means any person or entity that distributes -the Program.

- -

"Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

- -

"Program" means the Contributions distributed in accordance -with this Agreement.

- -

"Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

- -

2. GRANT OF RIGHTS

- -

a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

- -

b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

- -

c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

- -

d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

- -

3. REQUIREMENTS

- -

A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

- -

a) it complies with the terms and conditions of this -Agreement; and

- -

b) its license agreement:

- -

i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

- -

ii) effectively excludes on behalf of all Contributors -all liability for damages, including direct, indirect, special, -incidental and consequential damages, such as lost profits;

- -

iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

- -

iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

- -

When the Program is made available in source code form:

- -

a) it must be made available under this Agreement; and

- -

b) a copy of this Agreement must be included with each -copy of the Program.

- -

Contributors may not remove or alter any copyright notices contained -within the Program.

- -

Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

- -

4. COMMERCIAL DISTRIBUTION

- -

Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial -product offering should do so in a manner which does not create -potential liability for other Contributors. Therefore, if a Contributor -includes the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and -indemnify every other Contributor ("Indemnified Contributor") -against any losses, damages and costs (collectively "Losses") -arising from claims, lawsuits and other legal actions brought by a third -party against the Indemnified Contributor to the extent caused by the -acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. In -order to qualify, an Indemnified Contributor must: a) promptly notify -the Commercial Contributor in writing of such claim, and b) allow the -Commercial Contributor to control, and cooperate with the Commercial -Contributor in, the defense and any related settlement negotiations. The -Indemnified Contributor may participate in any such claim at its own -expense.

- -

For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

- -

5. NO WARRANTY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

- -

6. DISCLAIMER OF LIABILITY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

- -

7. GENERAL

- -

If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

- -

If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

- -

All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

- -

Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

- -

This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No party -to this Agreement will bring a legal action under this Agreement more -than one year after the cause of action arose. Each party waives its -rights to a jury trial in any resulting litigation.

- - - - \ No newline at end of file diff --git a/build-tools/maven/lib/aether-connector-basic-1.0.0.v20140518.jar b/build-tools/maven/lib/aether-connector-basic-1.0.0.v20140518.jar deleted file mode 100644 index 7a0045a971..0000000000 Binary files a/build-tools/maven/lib/aether-connector-basic-1.0.0.v20140518.jar and /dev/null differ diff --git a/build-tools/maven/lib/aether-connector-basic.license b/build-tools/maven/lib/aether-connector-basic.license deleted file mode 100644 index 3998fcebee..0000000000 --- a/build-tools/maven/lib/aether-connector-basic.license +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - -Eclipse Public License - Version 1.0 - - - - - - -

Eclipse Public License - v 1.0

- -

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

- -

1. DEFINITIONS

- -

"Contribution" means:

- -

a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

-

b) in the case of each subsequent Contributor:

-

i) changes to the Program, and

-

ii) additions to the Program;

-

where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

- -

"Contributor" means any person or entity that distributes -the Program.

- -

"Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

- -

"Program" means the Contributions distributed in accordance -with this Agreement.

- -

"Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

- -

2. GRANT OF RIGHTS

- -

a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

- -

b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

- -

c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

- -

d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

- -

3. REQUIREMENTS

- -

A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

- -

a) it complies with the terms and conditions of this -Agreement; and

- -

b) its license agreement:

- -

i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

- -

ii) effectively excludes on behalf of all Contributors -all liability for damages, including direct, indirect, special, -incidental and consequential damages, such as lost profits;

- -

iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

- -

iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

- -

When the Program is made available in source code form:

- -

a) it must be made available under this Agreement; and

- -

b) a copy of this Agreement must be included with each -copy of the Program.

- -

Contributors may not remove or alter any copyright notices contained -within the Program.

- -

Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

- -

4. COMMERCIAL DISTRIBUTION

- -

Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial -product offering should do so in a manner which does not create -potential liability for other Contributors. Therefore, if a Contributor -includes the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and -indemnify every other Contributor ("Indemnified Contributor") -against any losses, damages and costs (collectively "Losses") -arising from claims, lawsuits and other legal actions brought by a third -party against the Indemnified Contributor to the extent caused by the -acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. In -order to qualify, an Indemnified Contributor must: a) promptly notify -the Commercial Contributor in writing of such claim, and b) allow the -Commercial Contributor to control, and cooperate with the Commercial -Contributor in, the defense and any related settlement negotiations. The -Indemnified Contributor may participate in any such claim at its own -expense.

- -

For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

- -

5. NO WARRANTY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

- -

6. DISCLAIMER OF LIABILITY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

- -

7. GENERAL

- -

If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

- -

If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

- -

All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

- -

Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

- -

This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No party -to this Agreement will bring a legal action under this Agreement more -than one year after the cause of action arose. Each party waives its -rights to a jury trial in any resulting litigation.

- - - - \ No newline at end of file diff --git a/build-tools/maven/lib/aether-impl-1.0.0.v20140518.jar b/build-tools/maven/lib/aether-impl-1.0.0.v20140518.jar deleted file mode 100644 index 414199d7a9..0000000000 Binary files a/build-tools/maven/lib/aether-impl-1.0.0.v20140518.jar and /dev/null differ diff --git a/build-tools/maven/lib/aether-impl.license b/build-tools/maven/lib/aether-impl.license deleted file mode 100644 index 3998fcebee..0000000000 --- a/build-tools/maven/lib/aether-impl.license +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - -Eclipse Public License - Version 1.0 - - - - - - -

Eclipse Public License - v 1.0

- -

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

- -

1. DEFINITIONS

- -

"Contribution" means:

- -

a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

-

b) in the case of each subsequent Contributor:

-

i) changes to the Program, and

-

ii) additions to the Program;

-

where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

- -

"Contributor" means any person or entity that distributes -the Program.

- -

"Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

- -

"Program" means the Contributions distributed in accordance -with this Agreement.

- -

"Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

- -

2. GRANT OF RIGHTS

- -

a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

- -

b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

- -

c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

- -

d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

- -

3. REQUIREMENTS

- -

A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

- -

a) it complies with the terms and conditions of this -Agreement; and

- -

b) its license agreement:

- -

i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

- -

ii) effectively excludes on behalf of all Contributors -all liability for damages, including direct, indirect, special, -incidental and consequential damages, such as lost profits;

- -

iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

- -

iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

- -

When the Program is made available in source code form:

- -

a) it must be made available under this Agreement; and

- -

b) a copy of this Agreement must be included with each -copy of the Program.

- -

Contributors may not remove or alter any copyright notices contained -within the Program.

- -

Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

- -

4. COMMERCIAL DISTRIBUTION

- -

Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial -product offering should do so in a manner which does not create -potential liability for other Contributors. Therefore, if a Contributor -includes the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and -indemnify every other Contributor ("Indemnified Contributor") -against any losses, damages and costs (collectively "Losses") -arising from claims, lawsuits and other legal actions brought by a third -party against the Indemnified Contributor to the extent caused by the -acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. In -order to qualify, an Indemnified Contributor must: a) promptly notify -the Commercial Contributor in writing of such claim, and b) allow the -Commercial Contributor to control, and cooperate with the Commercial -Contributor in, the defense and any related settlement negotiations. The -Indemnified Contributor may participate in any such claim at its own -expense.

- -

For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

- -

5. NO WARRANTY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

- -

6. DISCLAIMER OF LIABILITY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

- -

7. GENERAL

- -

If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

- -

If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

- -

All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

- -

Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

- -

This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No party -to this Agreement will bring a legal action under this Agreement more -than one year after the cause of action arose. Each party waives its -rights to a jury trial in any resulting litigation.

- - - - \ No newline at end of file diff --git a/build-tools/maven/lib/aether-spi-1.0.0.v20140518.jar b/build-tools/maven/lib/aether-spi-1.0.0.v20140518.jar deleted file mode 100644 index 4f3fd700e1..0000000000 Binary files a/build-tools/maven/lib/aether-spi-1.0.0.v20140518.jar and /dev/null differ diff --git a/build-tools/maven/lib/aether-spi.license b/build-tools/maven/lib/aether-spi.license deleted file mode 100644 index 3998fcebee..0000000000 --- a/build-tools/maven/lib/aether-spi.license +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - -Eclipse Public License - Version 1.0 - - - - - - -

Eclipse Public License - v 1.0

- -

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

- -

1. DEFINITIONS

- -

"Contribution" means:

- -

a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

-

b) in the case of each subsequent Contributor:

-

i) changes to the Program, and

-

ii) additions to the Program;

-

where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

- -

"Contributor" means any person or entity that distributes -the Program.

- -

"Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

- -

"Program" means the Contributions distributed in accordance -with this Agreement.

- -

"Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

- -

2. GRANT OF RIGHTS

- -

a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

- -

b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

- -

c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

- -

d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

- -

3. REQUIREMENTS

- -

A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

- -

a) it complies with the terms and conditions of this -Agreement; and

- -

b) its license agreement:

- -

i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

- -

ii) effectively excludes on behalf of all Contributors -all liability for damages, including direct, indirect, special, -incidental and consequential damages, such as lost profits;

- -

iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

- -

iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

- -

When the Program is made available in source code form:

- -

a) it must be made available under this Agreement; and

- -

b) a copy of this Agreement must be included with each -copy of the Program.

- -

Contributors may not remove or alter any copyright notices contained -within the Program.

- -

Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

- -

4. COMMERCIAL DISTRIBUTION

- -

Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial -product offering should do so in a manner which does not create -potential liability for other Contributors. Therefore, if a Contributor -includes the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and -indemnify every other Contributor ("Indemnified Contributor") -against any losses, damages and costs (collectively "Losses") -arising from claims, lawsuits and other legal actions brought by a third -party against the Indemnified Contributor to the extent caused by the -acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. In -order to qualify, an Indemnified Contributor must: a) promptly notify -the Commercial Contributor in writing of such claim, and b) allow the -Commercial Contributor to control, and cooperate with the Commercial -Contributor in, the defense and any related settlement negotiations. The -Indemnified Contributor may participate in any such claim at its own -expense.

- -

For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

- -

5. NO WARRANTY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

- -

6. DISCLAIMER OF LIABILITY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

- -

7. GENERAL

- -

If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

- -

If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

- -

All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

- -

Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

- -

This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No party -to this Agreement will bring a legal action under this Agreement more -than one year after the cause of action arose. Each party waives its -rights to a jury trial in any resulting litigation.

- - - - \ No newline at end of file diff --git a/build-tools/maven/lib/aether-transport-wagon-1.0.0.v20140518.jar b/build-tools/maven/lib/aether-transport-wagon-1.0.0.v20140518.jar deleted file mode 100644 index 43c31edc8f..0000000000 Binary files a/build-tools/maven/lib/aether-transport-wagon-1.0.0.v20140518.jar and /dev/null differ diff --git a/build-tools/maven/lib/aether-transport-wagon.license b/build-tools/maven/lib/aether-transport-wagon.license deleted file mode 100644 index 3998fcebee..0000000000 --- a/build-tools/maven/lib/aether-transport-wagon.license +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - -Eclipse Public License - Version 1.0 - - - - - - -

Eclipse Public License - v 1.0

- -

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

- -

1. DEFINITIONS

- -

"Contribution" means:

- -

a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

-

b) in the case of each subsequent Contributor:

-

i) changes to the Program, and

-

ii) additions to the Program;

-

where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

- -

"Contributor" means any person or entity that distributes -the Program.

- -

"Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

- -

"Program" means the Contributions distributed in accordance -with this Agreement.

- -

"Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

- -

2. GRANT OF RIGHTS

- -

a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

- -

b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

- -

c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

- -

d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

- -

3. REQUIREMENTS

- -

A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

- -

a) it complies with the terms and conditions of this -Agreement; and

- -

b) its license agreement:

- -

i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

- -

ii) effectively excludes on behalf of all Contributors -all liability for damages, including direct, indirect, special, -incidental and consequential damages, such as lost profits;

- -

iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

- -

iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

- -

When the Program is made available in source code form:

- -

a) it must be made available under this Agreement; and

- -

b) a copy of this Agreement must be included with each -copy of the Program.

- -

Contributors may not remove or alter any copyright notices contained -within the Program.

- -

Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

- -

4. COMMERCIAL DISTRIBUTION

- -

Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial -product offering should do so in a manner which does not create -potential liability for other Contributors. Therefore, if a Contributor -includes the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and -indemnify every other Contributor ("Indemnified Contributor") -against any losses, damages and costs (collectively "Losses") -arising from claims, lawsuits and other legal actions brought by a third -party against the Indemnified Contributor to the extent caused by the -acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. In -order to qualify, an Indemnified Contributor must: a) promptly notify -the Commercial Contributor in writing of such claim, and b) allow the -Commercial Contributor to control, and cooperate with the Commercial -Contributor in, the defense and any related settlement negotiations. The -Indemnified Contributor may participate in any such claim at its own -expense.

- -

For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

- -

5. NO WARRANTY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

- -

6. DISCLAIMER OF LIABILITY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

- -

7. GENERAL

- -

If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

- -

If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

- -

All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

- -

Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

- -

This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No party -to this Agreement will bring a legal action under this Agreement more -than one year after the cause of action arose. Each party waives its -rights to a jury trial in any resulting litigation.

- - - - \ No newline at end of file diff --git a/build-tools/maven/lib/aether-util-1.0.0.v20140518.jar b/build-tools/maven/lib/aether-util-1.0.0.v20140518.jar deleted file mode 100644 index ceca79ac8a..0000000000 Binary files a/build-tools/maven/lib/aether-util-1.0.0.v20140518.jar and /dev/null differ diff --git a/build-tools/maven/lib/aether-util.license b/build-tools/maven/lib/aether-util.license deleted file mode 100644 index 3998fcebee..0000000000 --- a/build-tools/maven/lib/aether-util.license +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - -Eclipse Public License - Version 1.0 - - - - - - -

Eclipse Public License - v 1.0

- -

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

- -

1. DEFINITIONS

- -

"Contribution" means:

- -

a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

-

b) in the case of each subsequent Contributor:

-

i) changes to the Program, and

-

ii) additions to the Program;

-

where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

- -

"Contributor" means any person or entity that distributes -the Program.

- -

"Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

- -

"Program" means the Contributions distributed in accordance -with this Agreement.

- -

"Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

- -

2. GRANT OF RIGHTS

- -

a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

- -

b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

- -

c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

- -

d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

- -

3. REQUIREMENTS

- -

A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

- -

a) it complies with the terms and conditions of this -Agreement; and

- -

b) its license agreement:

- -

i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

- -

ii) effectively excludes on behalf of all Contributors -all liability for damages, including direct, indirect, special, -incidental and consequential damages, such as lost profits;

- -

iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

- -

iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

- -

When the Program is made available in source code form:

- -

a) it must be made available under this Agreement; and

- -

b) a copy of this Agreement must be included with each -copy of the Program.

- -

Contributors may not remove or alter any copyright notices contained -within the Program.

- -

Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

- -

4. COMMERCIAL DISTRIBUTION

- -

Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial -product offering should do so in a manner which does not create -potential liability for other Contributors. Therefore, if a Contributor -includes the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and -indemnify every other Contributor ("Indemnified Contributor") -against any losses, damages and costs (collectively "Losses") -arising from claims, lawsuits and other legal actions brought by a third -party against the Indemnified Contributor to the extent caused by the -acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. In -order to qualify, an Indemnified Contributor must: a) promptly notify -the Commercial Contributor in writing of such claim, and b) allow the -Commercial Contributor to control, and cooperate with the Commercial -Contributor in, the defense and any related settlement negotiations. The -Indemnified Contributor may participate in any such claim at its own -expense.

- -

For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

- -

5. NO WARRANTY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

- -

6. DISCLAIMER OF LIABILITY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

- -

7. GENERAL

- -

If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

- -

If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

- -

All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

- -

Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

- -

This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No party -to this Agreement will bring a legal action under this Agreement more -than one year after the cause of action arose. Each party waives its -rights to a jury trial in any resulting litigation.

- - - - \ No newline at end of file diff --git a/build-tools/maven/lib/aopalliance-1.0.jar b/build-tools/maven/lib/aopalliance-1.0.jar deleted file mode 100644 index 578b1a0c35..0000000000 Binary files a/build-tools/maven/lib/aopalliance-1.0.jar and /dev/null differ diff --git a/build-tools/maven/lib/cdi-api-1.0.jar b/build-tools/maven/lib/cdi-api-1.0.jar deleted file mode 100644 index fe240f6fd9..0000000000 Binary files a/build-tools/maven/lib/cdi-api-1.0.jar and /dev/null differ diff --git a/build-tools/maven/lib/cdi-api.license b/build-tools/maven/lib/cdi-api.license deleted file mode 100644 index ab190e5369..0000000000 --- a/build-tools/maven/lib/cdi-api.license +++ /dev/null @@ -1,445 +0,0 @@ - - - - Apache License, Version 2.0 - - - - - - - - - - - - - - - - - -
- - -
-

Apache License

Version 2.0, January 2004

-http://www.apache.org/licenses/

-

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

-

1. Definitions.

-

"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document.

-

"Licensor" shall mean the copyright owner or entity authorized by the -copyright owner that is granting the License.

-

"Legal Entity" shall mean the union of the acting entity and all other -entities that control, are controlled by, or are under common control with -that entity. For the purposes of this definition, "control" means (i) the -power, direct or indirect, to cause the direction or management of such -entity, whether by contract or otherwise, or (ii) ownership of fifty -percent (50%) or more of the outstanding shares, or (iii) beneficial -ownership of such entity.

-

"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License.

-

"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation source, -and configuration files.

-

"Object" form shall mean any form resulting from mechanical transformation -or translation of a Source form, including but not limited to compiled -object code, generated documentation, and conversions to other media types.

-

"Work" shall mean the work of authorship, whether in Source or Object form, -made available under the License, as indicated by a copyright notice that -is included in or attached to the work (an example is provided in the -Appendix below).

-

"Derivative Works" shall mean any work, whether in Source or Object form, -that is based on (or derived from) the Work and for which the editorial -revisions, annotations, elaborations, or other modifications represent, as -a whole, an original work of authorship. For the purposes of this License, -Derivative Works shall not include works that remain separable from, or -merely link (or bind by name) to the interfaces of, the Work and Derivative -Works thereof.

-

"Contribution" shall mean any work of authorship, including the original -version of the Work and any modifications or additions to that Work or -Derivative Works thereof, that is intentionally submitted to Licensor for -inclusion in the Work by the copyright owner or by an individual or Legal -Entity authorized to submit on behalf of the copyright owner. For the -purposes of this definition, "submitted" means any form of electronic, -verbal, or written communication sent to the Licensor or its -representatives, including but not limited to communication on electronic -mailing lists, source code control systems, and issue tracking systems that -are managed by, or on behalf of, the Licensor for the purpose of discussing -and improving the Work, but excluding communication that is conspicuously -marked or otherwise designated in writing by the copyright owner as "Not a -Contribution."

-

"Contributor" shall mean Licensor and any individual or Legal Entity on -behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work.

-

2. Grant of Copyright License. Subject to the -terms and conditions of this License, each Contributor hereby grants to You -a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, publicly -display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form.

-

3. Grant of Patent License. Subject to the terms -and conditions of this License, each Contributor hereby grants to You a -perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, use, -offer to sell, sell, import, and otherwise transfer the Work, where such -license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by -combination of their Contribution(s) with the Work to which such -Contribution(s) was submitted. If You institute patent litigation against -any entity (including a cross-claim or counterclaim in a lawsuit) alleging -that the Work or a Contribution incorporated within the Work constitutes -direct or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate as of the -date such litigation is filed.

-

4. Redistribution. You may reproduce and -distribute copies of the Work or Derivative Works thereof in any medium, -with or without modifications, and in Source or Object form, provided that -You meet the following conditions:

-
    -
  1. You must give any other recipients of the Work or Derivative Works a -copy of this License; and
  2. - -
  3. You must cause any modified files to carry prominent notices stating -that You changed the files; and
  4. - -
  5. You must retain, in the Source form of any Derivative Works that You -distribute, all copyright, patent, trademark, and attribution notices from -the Source form of the Work, excluding those notices that do not pertain to -any part of the Derivative Works; and
  6. - -
  7. If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy -of the attribution notices contained within such NOTICE file, excluding -those notices that do not pertain to any part of the Derivative Works, in -at least one of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or documentation, -if provided along with the Derivative Works; or, within a display generated -by the Derivative Works, if and wherever such third-party notices normally -appear. The contents of the NOTICE file are for informational purposes only -and do not modify the License. You may add Your own attribution notices -within Derivative Works that You distribute, alongside or as an addendum to -the NOTICE text from the Work, provided that such additional attribution -notices cannot be construed as modifying the License. -
    -
    -You may add Your own copyright statement to Your modifications and may -provide additional or different license terms and conditions for use, -reproduction, or distribution of Your modifications, or for any such -Derivative Works as a whole, provided Your use, reproduction, and -distribution of the Work otherwise complies with the conditions stated in -this License. -
  8. - -
- -

5. Submission of Contributions. Unless You -explicitly state otherwise, any Contribution intentionally submitted for -inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the -terms of any separate license agreement you may have executed with Licensor -regarding such Contributions.

-

6. Trademarks. This License does not grant -permission to use the trade names, trademarks, service marks, or product -names of the Licensor, except as required for reasonable and customary use -in describing the origin of the Work and reproducing the content of the -NOTICE file.

-

7. Disclaimer of Warranty. Unless required by -applicable law or agreed to in writing, Licensor provides the Work (and -each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT -WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, -without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You -are solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise -of permissions under this License.

-

8. Limitation of Liability. In no event and -under no legal theory, whether in tort (including negligence), contract, or -otherwise, unless required by applicable law (such as deliberate and -grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a result -of this License or out of the use or inability to use the Work (including -but not limited to damages for loss of goodwill, work stoppage, computer -failure or malfunction, or any and all other commercial damages or losses), -even if such Contributor has been advised of the possibility of such -damages.

-

9. Accepting Warranty or Additional Liability. -While redistributing the Work or Derivative Works thereof, You may choose -to offer, and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this License. -However, in accepting such obligations, You may act only on Your own behalf -and on Your sole responsibility, not on behalf of any other Contributor, -and only if You agree to indemnify, defend, and hold each Contributor -harmless for any liability incurred by, or claims asserted against, such -Contributor by reason of your accepting any such warranty or additional -liability.

-

END OF TERMS AND CONDITIONS

-

APPENDIX: How to apply the Apache License to your work

-

To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included -on the same "printed page" as the copyright notice for easier -identification within third-party archives.

-
Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-
-
- - - - diff --git a/build-tools/maven/lib/commons-cli-1.2.jar b/build-tools/maven/lib/commons-cli-1.2.jar deleted file mode 100644 index ce4b9fffe4..0000000000 Binary files a/build-tools/maven/lib/commons-cli-1.2.jar and /dev/null differ diff --git a/build-tools/maven/lib/commons-io-2.2.jar b/build-tools/maven/lib/commons-io-2.2.jar deleted file mode 100644 index 84ca565859..0000000000 Binary files a/build-tools/maven/lib/commons-io-2.2.jar and /dev/null differ diff --git a/build-tools/maven/lib/commons-lang-2.6.jar b/build-tools/maven/lib/commons-lang-2.6.jar deleted file mode 100644 index 98467d3a65..0000000000 Binary files a/build-tools/maven/lib/commons-lang-2.6.jar and /dev/null differ diff --git a/build-tools/maven/lib/ext/README.txt b/build-tools/maven/lib/ext/README.txt deleted file mode 100644 index 5ebe59eb39..0000000000 --- a/build-tools/maven/lib/ext/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -Use this directory to contribute 3rd-party extensions to the Maven core. These extensions can either extend or override -Maven's default implementation. diff --git a/build-tools/maven/lib/guava-18.0.jar b/build-tools/maven/lib/guava-18.0.jar deleted file mode 100644 index 8f89e49018..0000000000 Binary files a/build-tools/maven/lib/guava-18.0.jar and /dev/null differ diff --git a/build-tools/maven/lib/javax.inject-1.jar b/build-tools/maven/lib/javax.inject-1.jar deleted file mode 100644 index b2a9d0bf7b..0000000000 Binary files a/build-tools/maven/lib/javax.inject-1.jar and /dev/null differ diff --git a/build-tools/maven/lib/jsoup-1.7.2.jar b/build-tools/maven/lib/jsoup-1.7.2.jar deleted file mode 100644 index 1d9879f8da..0000000000 Binary files a/build-tools/maven/lib/jsoup-1.7.2.jar and /dev/null differ diff --git a/build-tools/maven/lib/jsoup.license b/build-tools/maven/lib/jsoup.license deleted file mode 100644 index 061928a8eb..0000000000 --- a/build-tools/maven/lib/jsoup.license +++ /dev/null @@ -1,66 +0,0 @@ - - - - jsoup License - - - - - - -
- - -
-
-

jsoup License

-

The jsoup code-base (include source and compiled packages) are distributed under the open source MIT license as described below.

-

The MIT License

-

Copyright © 2009 - 2014 Jonathan Hedley (jonathan@hedley.net)

-

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

-

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

-

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

-
- -
-
- -
- - -
- - - - - diff --git a/build-tools/maven/lib/jsr250-api-1.0.jar b/build-tools/maven/lib/jsr250-api-1.0.jar deleted file mode 100644 index c1f29bf844..0000000000 Binary files a/build-tools/maven/lib/jsr250-api-1.0.jar and /dev/null differ diff --git a/build-tools/maven/lib/jsr250-api.license b/build-tools/maven/lib/jsr250-api.license deleted file mode 100644 index a9194cf3cf..0000000000 --- a/build-tools/maven/lib/jsr250-api.license +++ /dev/null @@ -1,406 +0,0 @@ - - - - - CDDL ver. 1.0 - - - -COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 -1.
-
-Definitions. -
-
-1.1. Contributor means each -individual or entity that creates or -contributes to the creation of Modifications. -
-
-1.2. Contributor Version -means the combination of the Original -Software, prior Modifications used by a Contributor (if any), and the -Modifications made by that particular Contributor. -
-
-1.3. Covered Software means -(a) the Original Software, or (b) -Modifications, or (c) the combination of files containing Original -Software with files containing Modifications, in each case including -portions thereof. -
-
-1.4. Executable means the -Covered Software in any form other than -Source Code. -
-
-1.5. Initial Developer means -the individual or entity that first makes -Original Software available under this License. -
-
-1.6. Larger Work means a work -which combines Covered Software or -portions thereof with code not governed by the terms of this License. -
-
-1.7. License means this -document. -
-
-1.8. Licensable means having -the right to grant, to the maximum extent -possible, whether at the time of the initial grant or subsequently -acquired, any and all of the rights conveyed herein. -
-
-1.9. Modifications means the -Source Code and Executable form of any of -the following: -A. Any file that results from an addition to, deletion from or -modification of the contents of a file containing Original Software or -previous Modifications; -B. Any new file that contains any part of the Original Software or -previous Modification; or -C. Any new file that is contributed or otherwise made available under -the terms of this License. -
-
-1.10. Original Software means -the Source Code and Executable form of -computer software code that is originally released under this License. -
-
-1.11. Patent Claims means any -patent claim(s), now owned or hereafter -acquired, including without limitation, method, process, and apparatus -claims, in any patent Licensable by grantor. -
-
-1.12. Source Code means (a) -the common form of computer software code -in which modifications are made and (b) associated documentation -included in or with such code. -
-
-1.13. You (or Your) means an -individual or a legal entity exercising -rights under, and complying with all of the terms of, this License. For -legal entities, You includes any entity which controls, is controlled -by, or is under common control with You. For purposes of this -definition, control means (a) the power, direct or indirect, to -cause -the direction or management of such entity, whether by contract or -otherwise, or (b) ownership of more than fifty percent (50%) of -the -outstanding shares or beneficial ownership of such entity. -
-
-2. License Grants.
-
- 2.1. The Initial Developer Grant. -Conditioned upon Your compliance with Section 3.1 below and subject to -third party intellectual property claims, the Initial Developer hereby -grants You a world-wide, royalty-free, non-exclusive license: -
-
-(a) under intellectual property rights (other than patent or trademark) -Licensable by Initial Developer, to use, reproduce, modify, display, -perform, sublicense and distribute the Original Software (or portions -thereof), with or without Modifications, and/or as part of a Larger -Work; and -
-
-(b) under Patent Claims infringed by the making, using or selling of -Original Software, to make, have made, use, practice, sell, and offer -for sale, and/or otherwise dispose of the Original Software (or -portions thereof);
-
- (c) The licenses granted in Sections 2.1(a) and (b) are -effective on -the date Initial Developer first distributes or otherwise makes the -Original Software available to a third party under the terms of this -License;
-
- (d) Notwithstanding Section 2.1(b) above, no patent license -is granted: -(1) for code that You delete from the Original Software, or -(2) for -infringements caused by: (i) the modification of the Original -Software, -or (ii) the combination of the Original Software with other -software or -devices. -
-
-2.2. Contributor Grant. -Conditioned upon Your compliance with Section 3.1 below and subject to -third party intellectual property claims, -each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: -
-
-(a) under intellectual property rights (other than patent or trademark) -Licensable by Contributor to use, reproduce, modify, display, perform, -sublicense and distribute the Modifications created by such Contributor -(or portions thereof), either on an unmodified basis, with other -Modifications, as Covered Software and/or as part of a Larger Work; and -
-
-(b) under Patent Claims infringed by the making, using, or selling of -Modifications made by that Contributor either alone and/or in -combination with its Contributor Version (or portions of such -combination), to make, use, sell, offer for sale, have made, and/or -otherwise dispose of: (1) Modifications made by that Contributor -(or -portions thereof); and (2) the combination of Modifications made -by -that Contributor with its Contributor Version (or portions of such -combination). -
-
-(c) The licenses granted in Sections 2.2(a) and 2.2(b) are -effective on -the date Contributor first distributes or otherwise makes the -Modifications available to a third party.
-
-(d) Notwithstanding Section 2.2(b) above, no patent license is -granted: -(1) for any code that Contributor has deleted from the Contributor -Version; (2) for infringements caused by: (i) third party -modifications -of Contributor Version, or (ii) the combination of Modifications -made -by that Contributor with other software (except as part of the -Contributor Version) or other devices; or (3) under Patent Claims -infringed by Covered Software in the absence of Modifications made by -that Contributor. -
-
-3. Distribution Obligations. -
-
-3.1. Availability of Source Code. -Any Covered Software that You distribute or otherwise make available in -Executable form must also be made available in Source Code form and -that Source Code form must be distributed only under the terms of this -License. You must include a copy of this License with every copy of the -Source Code form of the Covered Software You distribute or otherwise -make available. You must inform recipients of any such Covered Software -in Executable form as to how they can obtain such Covered Software in -Source Code form in a reasonable manner on or through a medium -customarily used for software exchange. -
-
-3.2. Modifications. -The Modifications that You create or to which You contribute are -governed by the terms of this License. You represent that You believe -Your Modifications are Your original creation(s) and/or You have -sufficient rights to grant the rights conveyed by this License. -
-
-3.3. Required Notices. -You must include a notice in each of Your Modifications that identifies -You as the Contributor of the Modification. You may not remove or alter -any copyright, patent or trademark notices contained within the Covered -Software, or any notices of licensing or any descriptive text giving -attribution to any Contributor or the Initial Developer. -
-
-3.4. Application of Additional Terms. -You may not offer or impose any terms on any Covered Software in Source -Code form that alters or restricts the applicable version of this -License or the recipients rights hereunder. You may choose to offer, -and to charge a fee for, warranty, support, indemnity or liability -obligations to one or more recipients of Covered Software. However, you -may do so only on Your own behalf, and not on behalf of the Initial -Developer or any Contributor. You must make it absolutely clear that -any such warranty, support, indemnity or liability obligation is -offered by You alone, and You hereby agree to indemnify the Initial -Developer and every Contributor for any liability incurred by the -Initial Developer or such Contributor as a result of warranty, support, -indemnity or liability terms You offer.
-
-3.5. Distribution of Executable Versions. -You may distribute the Executable form of the Covered Software under -the terms of this License or under the terms of a license of Your -choice, which may contain terms different from this License, provided -that You are in compliance with the terms of this License and that the -license for the Executable form -does not attempt to limit or alter the recipients rights in the Source -Code form from the rights set forth in this License. If You distribute -the Covered Software in Executable form under a different license, You -must make it absolutely clear that any terms which differ from this -License are offered by You alone, not by the Initial Developer or -Contributor. You hereby agree to indemnify the Initial Developer and -every Contributor for any liability incurred by the Initial Developer -or such Contributor as a result of any such terms You offer. -
-
-3.6. Larger Works. -You may create a Larger Work by combining Covered Software with other -code not governed by the terms of this License and distribute the -Larger Work as a single product. In such a case, You must make sure the -requirements of this License are fulfilled for the Covered Software. -
-
-4. Versions of the License. -
-
-4.1. New Versions. -Sun Microsystems, Inc. is the initial license steward and may publish -revised and/or new versions of this License from time to time. Each -version will be given a distinguishing version number. Except as -provided in Section 4.3, no one other than the license steward has the -right to modify this License. -
-
-4.2. Effect of New Versions. -You may always continue to use, distribute or otherwise make the -Covered Software available under the terms of the version of the -License under which You originally received the Covered Software. If -the Initial Developer includes a notice in the Original Software -prohibiting it from being distributed or otherwise made available under -any subsequent version of the License, You must distribute and make the -Covered Software available under the terms of the version of the -License under which You originally received the Covered Software. -Otherwise, You may also choose to use, distribute or otherwise make the -Covered Software available under the terms of any subsequent version of -the License published by the license steward. -
-
-4.3. Modified Versions. -When You are an Initial Developer and You want to create a new license -for Your Original Software, You may create and use a modified version -of this License if You: (a) rename the license and remove any -references to the name of the license steward (except to note that the -license differs from this License); and (b) otherwise make it -clear -that the license contains terms which differ from this License. -
-
-5. DISCLAIMER OF WARRANTY. -COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN AS IS BASIS, -WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, -WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF -DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. -THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN -ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) -ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS -DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. -NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER -THIS DISCLAIMER. -
-
-6. TERMINATION. -
-
-6.1. This License and the rights granted hereunder will terminate -automatically if You fail to comply with terms herein and fail to cure -such breach within 30 days of becoming aware of the breach. Provisions -which, by their nature, must remain in effect beyond the termination of -this License shall survive. -
-
-6.2. If You assert a patent infringement claim (excluding declaratory -judgment actions) against Initial Developer or a Contributor (the -Initial Developer or Contributor against whom You assert such claim is -referred to as Participant) alleging that the Participant Software -(meaning the Contributor Version where the Participant is a Contributor -or the Original Software where the Participant is the Initial -Developer) directly or indirectly infringes any patent, then any and -all rights granted directly or indirectly to You by such Participant, -the Initial Developer (if the Initial Developer is not the Participant) -and all Contributors under -Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice -from -Participant terminate prospectively and automatically at the expiration -of such 60 day notice period, unless if within such 60 day period You -withdraw Your claim with respect to the Participant Software against -such Participant either unilaterally or pursuant to a written agreement -with Participant. -
-
-6.3. In the event of termination under Sections 6.1 or 6.2 above, -all -end user licenses that have been validly granted by You or any -distributor hereunder prior to termination (excluding licenses granted -to You by any distributor) shall survive termination. -
-
-7. LIMITATION OF LIABILITY. -UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT -(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL -DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED -SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY -PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST -PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR -MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF -SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. -THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR -PERSONAL INJURY RESULTING FROM SUCH PARTYS NEGLIGENCE TO THE EXTENT -APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT -ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL -DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. -
-
-8. U.S. GOVERNMENT END USERS. -The Covered Software is a commercial item, as that term is defined in -48 C.F.R. 2.101 (Oct. 1995), consisting of commercial -computer software -(as that term is defined at 48 C.F.R.  252.227-7014(a)(1)) and -commercial computer software documentation as such terms are used in -48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. -12.212 and 48 -C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government -End Users acquire Covered Software with only those rights set forth -herein. This U.S. Government Rights clause is in lieu of, and -supersedes, any other FAR, DFAR, or other clause or provision that -addresses Government rights in computer software under this License. -
-
-9. MISCELLANEOUS. -This License represents the complete agreement concerning subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. This License shall be governed by the -law of the jurisdiction specified in a notice contained within the -Original Software (except to the extent applicable law, if any, -provides otherwise), excluding such jurisdictions conflict-of-law -provisions. Any litigation relating to this License shall be subject to -the jurisdiction of the courts located in the jurisdiction and venue -specified in a notice contained within the Original Software, with the -losing party responsible for costs, including, without limitation, -court costs and reasonable attorneys fees and expenses. The application -of the United Nations Convention on Contracts for the International -Sale of Goods is expressly excluded. Any law or regulation which -provides that the language of a contract shall be construed against the -drafter shall not apply to this License. You agree that You alone are -responsible for compliance with the United States export administration -regulations (and the export control laws and regulation of any other -countries) when You use, distribute or otherwise make available any -Covered Software.
-
-10. RESPONSIBILITY FOR CLAIMS. -As between Initial Developer and the Contributors, each party is -responsible for claims and damages arising, directly or indirectly, out -of its utilization of rights under this License and You agree to work -with Initial Developer and Contributors to distribute such -responsibility on an equitable basis. Nothing herein is intended or -shall be deemed to constitute any admission of liability. -
-
-NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION -LICENSE (CDDL) -The code released under the CDDL shall be governed by the -laws of the State of California (excluding conflict-of-law provisions). -Any litigation relating to this License shall be subject to the -jurisdiction of the Federal Courts of the Northern District of -California and the state courts of the State of California, with venue -lying in Santa Clara County, California. - - diff --git a/build-tools/maven/lib/maven-aether-provider-3.2.5.jar b/build-tools/maven/lib/maven-aether-provider-3.2.5.jar deleted file mode 100644 index 0abe65e586..0000000000 Binary files a/build-tools/maven/lib/maven-aether-provider-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-aether-provider.license b/build-tools/maven/lib/maven-aether-provider.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-aether-provider.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/maven-artifact-3.2.5.jar b/build-tools/maven/lib/maven-artifact-3.2.5.jar deleted file mode 100644 index a90e72d6db..0000000000 Binary files a/build-tools/maven/lib/maven-artifact-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-artifact.license b/build-tools/maven/lib/maven-artifact.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-artifact.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/maven-compat-3.2.5.jar b/build-tools/maven/lib/maven-compat-3.2.5.jar deleted file mode 100644 index 2d2e0469ae..0000000000 Binary files a/build-tools/maven/lib/maven-compat-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-compat.license b/build-tools/maven/lib/maven-compat.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-compat.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/maven-core-3.2.5.jar b/build-tools/maven/lib/maven-core-3.2.5.jar deleted file mode 100644 index 18853458ef..0000000000 Binary files a/build-tools/maven/lib/maven-core-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-core.license b/build-tools/maven/lib/maven-core.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-core.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/maven-embedder-3.2.5.jar b/build-tools/maven/lib/maven-embedder-3.2.5.jar deleted file mode 100644 index 337dd879c0..0000000000 Binary files a/build-tools/maven/lib/maven-embedder-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-embedder.license b/build-tools/maven/lib/maven-embedder.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-embedder.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/maven-model-3.2.5.jar b/build-tools/maven/lib/maven-model-3.2.5.jar deleted file mode 100644 index 04eeeabf5c..0000000000 Binary files a/build-tools/maven/lib/maven-model-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-model-builder-3.2.5.jar b/build-tools/maven/lib/maven-model-builder-3.2.5.jar deleted file mode 100644 index 5a62f9c9a3..0000000000 Binary files a/build-tools/maven/lib/maven-model-builder-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-model-builder.license b/build-tools/maven/lib/maven-model-builder.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-model-builder.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/maven-model.license b/build-tools/maven/lib/maven-model.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-model.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/maven-plugin-api-3.2.5.jar b/build-tools/maven/lib/maven-plugin-api-3.2.5.jar deleted file mode 100644 index 37e847b661..0000000000 Binary files a/build-tools/maven/lib/maven-plugin-api-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-plugin-api.license b/build-tools/maven/lib/maven-plugin-api.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-plugin-api.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/maven-repository-metadata-3.2.5.jar b/build-tools/maven/lib/maven-repository-metadata-3.2.5.jar deleted file mode 100644 index 9a0fb9782d..0000000000 Binary files a/build-tools/maven/lib/maven-repository-metadata-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-repository-metadata.license b/build-tools/maven/lib/maven-repository-metadata.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-repository-metadata.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/maven-settings-3.2.5.jar b/build-tools/maven/lib/maven-settings-3.2.5.jar deleted file mode 100644 index d99362cc75..0000000000 Binary files a/build-tools/maven/lib/maven-settings-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-settings-builder-3.2.5.jar b/build-tools/maven/lib/maven-settings-builder-3.2.5.jar deleted file mode 100644 index e6297df405..0000000000 Binary files a/build-tools/maven/lib/maven-settings-builder-3.2.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/maven-settings-builder.license b/build-tools/maven/lib/maven-settings-builder.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-settings-builder.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/maven-settings.license b/build-tools/maven/lib/maven-settings.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/maven-settings.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/org.eclipse.sisu.inject-0.3.0.M1.jar b/build-tools/maven/lib/org.eclipse.sisu.inject-0.3.0.M1.jar deleted file mode 100644 index 6c147f212f..0000000000 Binary files a/build-tools/maven/lib/org.eclipse.sisu.inject-0.3.0.M1.jar and /dev/null differ diff --git a/build-tools/maven/lib/org.eclipse.sisu.inject.license b/build-tools/maven/lib/org.eclipse.sisu.inject.license deleted file mode 100644 index 3998fcebee..0000000000 --- a/build-tools/maven/lib/org.eclipse.sisu.inject.license +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - -Eclipse Public License - Version 1.0 - - - - - - -

Eclipse Public License - v 1.0

- -

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

- -

1. DEFINITIONS

- -

"Contribution" means:

- -

a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

-

b) in the case of each subsequent Contributor:

-

i) changes to the Program, and

-

ii) additions to the Program;

-

where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

- -

"Contributor" means any person or entity that distributes -the Program.

- -

"Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

- -

"Program" means the Contributions distributed in accordance -with this Agreement.

- -

"Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

- -

2. GRANT OF RIGHTS

- -

a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

- -

b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

- -

c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

- -

d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

- -

3. REQUIREMENTS

- -

A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

- -

a) it complies with the terms and conditions of this -Agreement; and

- -

b) its license agreement:

- -

i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

- -

ii) effectively excludes on behalf of all Contributors -all liability for damages, including direct, indirect, special, -incidental and consequential damages, such as lost profits;

- -

iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

- -

iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

- -

When the Program is made available in source code form:

- -

a) it must be made available under this Agreement; and

- -

b) a copy of this Agreement must be included with each -copy of the Program.

- -

Contributors may not remove or alter any copyright notices contained -within the Program.

- -

Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

- -

4. COMMERCIAL DISTRIBUTION

- -

Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial -product offering should do so in a manner which does not create -potential liability for other Contributors. Therefore, if a Contributor -includes the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and -indemnify every other Contributor ("Indemnified Contributor") -against any losses, damages and costs (collectively "Losses") -arising from claims, lawsuits and other legal actions brought by a third -party against the Indemnified Contributor to the extent caused by the -acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. In -order to qualify, an Indemnified Contributor must: a) promptly notify -the Commercial Contributor in writing of such claim, and b) allow the -Commercial Contributor to control, and cooperate with the Commercial -Contributor in, the defense and any related settlement negotiations. The -Indemnified Contributor may participate in any such claim at its own -expense.

- -

For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

- -

5. NO WARRANTY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

- -

6. DISCLAIMER OF LIABILITY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

- -

7. GENERAL

- -

If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

- -

If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

- -

All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

- -

Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

- -

This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No party -to this Agreement will bring a legal action under this Agreement more -than one year after the cause of action arose. Each party waives its -rights to a jury trial in any resulting litigation.

- - - - \ No newline at end of file diff --git a/build-tools/maven/lib/org.eclipse.sisu.plexus-0.3.0.M1.jar b/build-tools/maven/lib/org.eclipse.sisu.plexus-0.3.0.M1.jar deleted file mode 100644 index ce4806c4f1..0000000000 Binary files a/build-tools/maven/lib/org.eclipse.sisu.plexus-0.3.0.M1.jar and /dev/null differ diff --git a/build-tools/maven/lib/org.eclipse.sisu.plexus.license b/build-tools/maven/lib/org.eclipse.sisu.plexus.license deleted file mode 100644 index 3998fcebee..0000000000 --- a/build-tools/maven/lib/org.eclipse.sisu.plexus.license +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - -Eclipse Public License - Version 1.0 - - - - - - -

Eclipse Public License - v 1.0

- -

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

- -

1. DEFINITIONS

- -

"Contribution" means:

- -

a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

-

b) in the case of each subsequent Contributor:

-

i) changes to the Program, and

-

ii) additions to the Program;

-

where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

- -

"Contributor" means any person or entity that distributes -the Program.

- -

"Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

- -

"Program" means the Contributions distributed in accordance -with this Agreement.

- -

"Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

- -

2. GRANT OF RIGHTS

- -

a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

- -

b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

- -

c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

- -

d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

- -

3. REQUIREMENTS

- -

A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

- -

a) it complies with the terms and conditions of this -Agreement; and

- -

b) its license agreement:

- -

i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

- -

ii) effectively excludes on behalf of all Contributors -all liability for damages, including direct, indirect, special, -incidental and consequential damages, such as lost profits;

- -

iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

- -

iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

- -

When the Program is made available in source code form:

- -

a) it must be made available under this Agreement; and

- -

b) a copy of this Agreement must be included with each -copy of the Program.

- -

Contributors may not remove or alter any copyright notices contained -within the Program.

- -

Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

- -

4. COMMERCIAL DISTRIBUTION

- -

Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial -product offering should do so in a manner which does not create -potential liability for other Contributors. Therefore, if a Contributor -includes the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and -indemnify every other Contributor ("Indemnified Contributor") -against any losses, damages and costs (collectively "Losses") -arising from claims, lawsuits and other legal actions brought by a third -party against the Indemnified Contributor to the extent caused by the -acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. In -order to qualify, an Indemnified Contributor must: a) promptly notify -the Commercial Contributor in writing of such claim, and b) allow the -Commercial Contributor to control, and cooperate with the Commercial -Contributor in, the defense and any related settlement negotiations. The -Indemnified Contributor may participate in any such claim at its own -expense.

- -

For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

- -

5. NO WARRANTY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

- -

6. DISCLAIMER OF LIABILITY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

- -

7. GENERAL

- -

If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

- -

If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

- -

All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

- -

Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

- -

This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No party -to this Agreement will bring a legal action under this Agreement more -than one year after the cause of action arose. Each party waives its -rights to a jury trial in any resulting litigation.

- - - - \ No newline at end of file diff --git a/build-tools/maven/lib/plexus-cipher-1.7.jar b/build-tools/maven/lib/plexus-cipher-1.7.jar deleted file mode 100644 index 21928b9851..0000000000 Binary files a/build-tools/maven/lib/plexus-cipher-1.7.jar and /dev/null differ diff --git a/build-tools/maven/lib/plexus-cipher.license b/build-tools/maven/lib/plexus-cipher.license deleted file mode 100644 index ab190e5369..0000000000 --- a/build-tools/maven/lib/plexus-cipher.license +++ /dev/null @@ -1,445 +0,0 @@ - - - - Apache License, Version 2.0 - - - - - - - - - - - - - - - - - -
- - -
-

Apache License

Version 2.0, January 2004

-http://www.apache.org/licenses/

-

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

-

1. Definitions.

-

"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document.

-

"Licensor" shall mean the copyright owner or entity authorized by the -copyright owner that is granting the License.

-

"Legal Entity" shall mean the union of the acting entity and all other -entities that control, are controlled by, or are under common control with -that entity. For the purposes of this definition, "control" means (i) the -power, direct or indirect, to cause the direction or management of such -entity, whether by contract or otherwise, or (ii) ownership of fifty -percent (50%) or more of the outstanding shares, or (iii) beneficial -ownership of such entity.

-

"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License.

-

"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation source, -and configuration files.

-

"Object" form shall mean any form resulting from mechanical transformation -or translation of a Source form, including but not limited to compiled -object code, generated documentation, and conversions to other media types.

-

"Work" shall mean the work of authorship, whether in Source or Object form, -made available under the License, as indicated by a copyright notice that -is included in or attached to the work (an example is provided in the -Appendix below).

-

"Derivative Works" shall mean any work, whether in Source or Object form, -that is based on (or derived from) the Work and for which the editorial -revisions, annotations, elaborations, or other modifications represent, as -a whole, an original work of authorship. For the purposes of this License, -Derivative Works shall not include works that remain separable from, or -merely link (or bind by name) to the interfaces of, the Work and Derivative -Works thereof.

-

"Contribution" shall mean any work of authorship, including the original -version of the Work and any modifications or additions to that Work or -Derivative Works thereof, that is intentionally submitted to Licensor for -inclusion in the Work by the copyright owner or by an individual or Legal -Entity authorized to submit on behalf of the copyright owner. For the -purposes of this definition, "submitted" means any form of electronic, -verbal, or written communication sent to the Licensor or its -representatives, including but not limited to communication on electronic -mailing lists, source code control systems, and issue tracking systems that -are managed by, or on behalf of, the Licensor for the purpose of discussing -and improving the Work, but excluding communication that is conspicuously -marked or otherwise designated in writing by the copyright owner as "Not a -Contribution."

-

"Contributor" shall mean Licensor and any individual or Legal Entity on -behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work.

-

2. Grant of Copyright License. Subject to the -terms and conditions of this License, each Contributor hereby grants to You -a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, publicly -display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form.

-

3. Grant of Patent License. Subject to the terms -and conditions of this License, each Contributor hereby grants to You a -perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, use, -offer to sell, sell, import, and otherwise transfer the Work, where such -license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by -combination of their Contribution(s) with the Work to which such -Contribution(s) was submitted. If You institute patent litigation against -any entity (including a cross-claim or counterclaim in a lawsuit) alleging -that the Work or a Contribution incorporated within the Work constitutes -direct or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate as of the -date such litigation is filed.

-

4. Redistribution. You may reproduce and -distribute copies of the Work or Derivative Works thereof in any medium, -with or without modifications, and in Source or Object form, provided that -You meet the following conditions:

-
    -
  1. You must give any other recipients of the Work or Derivative Works a -copy of this License; and
  2. - -
  3. You must cause any modified files to carry prominent notices stating -that You changed the files; and
  4. - -
  5. You must retain, in the Source form of any Derivative Works that You -distribute, all copyright, patent, trademark, and attribution notices from -the Source form of the Work, excluding those notices that do not pertain to -any part of the Derivative Works; and
  6. - -
  7. If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy -of the attribution notices contained within such NOTICE file, excluding -those notices that do not pertain to any part of the Derivative Works, in -at least one of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or documentation, -if provided along with the Derivative Works; or, within a display generated -by the Derivative Works, if and wherever such third-party notices normally -appear. The contents of the NOTICE file are for informational purposes only -and do not modify the License. You may add Your own attribution notices -within Derivative Works that You distribute, alongside or as an addendum to -the NOTICE text from the Work, provided that such additional attribution -notices cannot be construed as modifying the License. -
    -
    -You may add Your own copyright statement to Your modifications and may -provide additional or different license terms and conditions for use, -reproduction, or distribution of Your modifications, or for any such -Derivative Works as a whole, provided Your use, reproduction, and -distribution of the Work otherwise complies with the conditions stated in -this License. -
  8. - -
- -

5. Submission of Contributions. Unless You -explicitly state otherwise, any Contribution intentionally submitted for -inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the -terms of any separate license agreement you may have executed with Licensor -regarding such Contributions.

-

6. Trademarks. This License does not grant -permission to use the trade names, trademarks, service marks, or product -names of the Licensor, except as required for reasonable and customary use -in describing the origin of the Work and reproducing the content of the -NOTICE file.

-

7. Disclaimer of Warranty. Unless required by -applicable law or agreed to in writing, Licensor provides the Work (and -each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT -WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, -without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You -are solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise -of permissions under this License.

-

8. Limitation of Liability. In no event and -under no legal theory, whether in tort (including negligence), contract, or -otherwise, unless required by applicable law (such as deliberate and -grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a result -of this License or out of the use or inability to use the Work (including -but not limited to damages for loss of goodwill, work stoppage, computer -failure or malfunction, or any and all other commercial damages or losses), -even if such Contributor has been advised of the possibility of such -damages.

-

9. Accepting Warranty or Additional Liability. -While redistributing the Work or Derivative Works thereof, You may choose -to offer, and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this License. -However, in accepting such obligations, You may act only on Your own behalf -and on Your sole responsibility, not on behalf of any other Contributor, -and only if You agree to indemnify, defend, and hold each Contributor -harmless for any liability incurred by, or claims asserted against, such -Contributor by reason of your accepting any such warranty or additional -liability.

-

END OF TERMS AND CONDITIONS

-

APPENDIX: How to apply the Apache License to your work

-

To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included -on the same "printed page" as the copyright notice for easier -identification within third-party archives.

-
Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-
-
- - - - diff --git a/build-tools/maven/lib/plexus-component-annotations-1.5.5.jar b/build-tools/maven/lib/plexus-component-annotations-1.5.5.jar deleted file mode 100644 index e4de16fef8..0000000000 Binary files a/build-tools/maven/lib/plexus-component-annotations-1.5.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/plexus-interpolation-1.21.jar b/build-tools/maven/lib/plexus-interpolation-1.21.jar deleted file mode 100644 index 8a681ef3c3..0000000000 Binary files a/build-tools/maven/lib/plexus-interpolation-1.21.jar and /dev/null differ diff --git a/build-tools/maven/lib/plexus-sec-dispatcher-1.3.jar b/build-tools/maven/lib/plexus-sec-dispatcher-1.3.jar deleted file mode 100644 index 9dc9f64b27..0000000000 Binary files a/build-tools/maven/lib/plexus-sec-dispatcher-1.3.jar and /dev/null differ diff --git a/build-tools/maven/lib/plexus-sec-dispatcher.license b/build-tools/maven/lib/plexus-sec-dispatcher.license deleted file mode 100644 index ab190e5369..0000000000 --- a/build-tools/maven/lib/plexus-sec-dispatcher.license +++ /dev/null @@ -1,445 +0,0 @@ - - - - Apache License, Version 2.0 - - - - - - - - - - - - - - - - - -
- - -
-

Apache License

Version 2.0, January 2004

-http://www.apache.org/licenses/

-

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

-

1. Definitions.

-

"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document.

-

"Licensor" shall mean the copyright owner or entity authorized by the -copyright owner that is granting the License.

-

"Legal Entity" shall mean the union of the acting entity and all other -entities that control, are controlled by, or are under common control with -that entity. For the purposes of this definition, "control" means (i) the -power, direct or indirect, to cause the direction or management of such -entity, whether by contract or otherwise, or (ii) ownership of fifty -percent (50%) or more of the outstanding shares, or (iii) beneficial -ownership of such entity.

-

"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License.

-

"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation source, -and configuration files.

-

"Object" form shall mean any form resulting from mechanical transformation -or translation of a Source form, including but not limited to compiled -object code, generated documentation, and conversions to other media types.

-

"Work" shall mean the work of authorship, whether in Source or Object form, -made available under the License, as indicated by a copyright notice that -is included in or attached to the work (an example is provided in the -Appendix below).

-

"Derivative Works" shall mean any work, whether in Source or Object form, -that is based on (or derived from) the Work and for which the editorial -revisions, annotations, elaborations, or other modifications represent, as -a whole, an original work of authorship. For the purposes of this License, -Derivative Works shall not include works that remain separable from, or -merely link (or bind by name) to the interfaces of, the Work and Derivative -Works thereof.

-

"Contribution" shall mean any work of authorship, including the original -version of the Work and any modifications or additions to that Work or -Derivative Works thereof, that is intentionally submitted to Licensor for -inclusion in the Work by the copyright owner or by an individual or Legal -Entity authorized to submit on behalf of the copyright owner. For the -purposes of this definition, "submitted" means any form of electronic, -verbal, or written communication sent to the Licensor or its -representatives, including but not limited to communication on electronic -mailing lists, source code control systems, and issue tracking systems that -are managed by, or on behalf of, the Licensor for the purpose of discussing -and improving the Work, but excluding communication that is conspicuously -marked or otherwise designated in writing by the copyright owner as "Not a -Contribution."

-

"Contributor" shall mean Licensor and any individual or Legal Entity on -behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work.

-

2. Grant of Copyright License. Subject to the -terms and conditions of this License, each Contributor hereby grants to You -a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, publicly -display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form.

-

3. Grant of Patent License. Subject to the terms -and conditions of this License, each Contributor hereby grants to You a -perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, use, -offer to sell, sell, import, and otherwise transfer the Work, where such -license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by -combination of their Contribution(s) with the Work to which such -Contribution(s) was submitted. If You institute patent litigation against -any entity (including a cross-claim or counterclaim in a lawsuit) alleging -that the Work or a Contribution incorporated within the Work constitutes -direct or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate as of the -date such litigation is filed.

-

4. Redistribution. You may reproduce and -distribute copies of the Work or Derivative Works thereof in any medium, -with or without modifications, and in Source or Object form, provided that -You meet the following conditions:

-
    -
  1. You must give any other recipients of the Work or Derivative Works a -copy of this License; and
  2. - -
  3. You must cause any modified files to carry prominent notices stating -that You changed the files; and
  4. - -
  5. You must retain, in the Source form of any Derivative Works that You -distribute, all copyright, patent, trademark, and attribution notices from -the Source form of the Work, excluding those notices that do not pertain to -any part of the Derivative Works; and
  6. - -
  7. If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy -of the attribution notices contained within such NOTICE file, excluding -those notices that do not pertain to any part of the Derivative Works, in -at least one of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or documentation, -if provided along with the Derivative Works; or, within a display generated -by the Derivative Works, if and wherever such third-party notices normally -appear. The contents of the NOTICE file are for informational purposes only -and do not modify the License. You may add Your own attribution notices -within Derivative Works that You distribute, alongside or as an addendum to -the NOTICE text from the Work, provided that such additional attribution -notices cannot be construed as modifying the License. -
    -
    -You may add Your own copyright statement to Your modifications and may -provide additional or different license terms and conditions for use, -reproduction, or distribution of Your modifications, or for any such -Derivative Works as a whole, provided Your use, reproduction, and -distribution of the Work otherwise complies with the conditions stated in -this License. -
  8. - -
- -

5. Submission of Contributions. Unless You -explicitly state otherwise, any Contribution intentionally submitted for -inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the -terms of any separate license agreement you may have executed with Licensor -regarding such Contributions.

-

6. Trademarks. This License does not grant -permission to use the trade names, trademarks, service marks, or product -names of the Licensor, except as required for reasonable and customary use -in describing the origin of the Work and reproducing the content of the -NOTICE file.

-

7. Disclaimer of Warranty. Unless required by -applicable law or agreed to in writing, Licensor provides the Work (and -each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT -WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, -without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You -are solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise -of permissions under this License.

-

8. Limitation of Liability. In no event and -under no legal theory, whether in tort (including negligence), contract, or -otherwise, unless required by applicable law (such as deliberate and -grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a result -of this License or out of the use or inability to use the Work (including -but not limited to damages for loss of goodwill, work stoppage, computer -failure or malfunction, or any and all other commercial damages or losses), -even if such Contributor has been advised of the possibility of such -damages.

-

9. Accepting Warranty or Additional Liability. -While redistributing the Work or Derivative Works thereof, You may choose -to offer, and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this License. -However, in accepting such obligations, You may act only on Your own behalf -and on Your sole responsibility, not on behalf of any other Contributor, -and only if You agree to indemnify, defend, and hold each Contributor -harmless for any liability incurred by, or claims asserted against, such -Contributor by reason of your accepting any such warranty or additional -liability.

-

END OF TERMS AND CONDITIONS

-

APPENDIX: How to apply the Apache License to your work

-

To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included -on the same "printed page" as the copyright notice for easier -identification within third-party archives.

-
Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-
-
- - - - diff --git a/build-tools/maven/lib/plexus-utils-3.0.20.jar b/build-tools/maven/lib/plexus-utils-3.0.20.jar deleted file mode 100644 index d2f340b1d7..0000000000 Binary files a/build-tools/maven/lib/plexus-utils-3.0.20.jar and /dev/null differ diff --git a/build-tools/maven/lib/sisu-guice-3.2.3-no_aop.jar b/build-tools/maven/lib/sisu-guice-3.2.3-no_aop.jar deleted file mode 100644 index f4db997cef..0000000000 Binary files a/build-tools/maven/lib/sisu-guice-3.2.3-no_aop.jar and /dev/null differ diff --git a/build-tools/maven/lib/slf4j-api-1.7.5.jar b/build-tools/maven/lib/slf4j-api-1.7.5.jar deleted file mode 100644 index 8f004d3906..0000000000 Binary files a/build-tools/maven/lib/slf4j-api-1.7.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/slf4j-api.license b/build-tools/maven/lib/slf4j-api.license deleted file mode 100644 index 93f38931e3..0000000000 --- a/build-tools/maven/lib/slf4j-api.license +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - - - - - The MIT License (MIT) | Open Source Initiative - - - - - - - - - - - - -
-
- - - - - -
-

You are here

-

The MIT License (MIT)

-
-
-
- - -
-
- - - - -
- [OSI Approved License] -

The MIT License (MIT)

-

Copyright (c) <year> <copyright holders>

-

Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:

-

The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.

-

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

-
- -
- - -
- -
-
-
-
-
- -
- - -
-
- - diff --git a/build-tools/maven/lib/slf4j-simple-1.7.5.jar b/build-tools/maven/lib/slf4j-simple-1.7.5.jar deleted file mode 100644 index eca11dd417..0000000000 Binary files a/build-tools/maven/lib/slf4j-simple-1.7.5.jar and /dev/null differ diff --git a/build-tools/maven/lib/slf4j-simple.license b/build-tools/maven/lib/slf4j-simple.license deleted file mode 100644 index 93f38931e3..0000000000 --- a/build-tools/maven/lib/slf4j-simple.license +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - - - - - The MIT License (MIT) | Open Source Initiative - - - - - - - - - - - - -
-
- - - - - -
-

You are here

-

The MIT License (MIT)

-
-
-
- - -
-
- - - - -
- [OSI Approved License] -

The MIT License (MIT)

-

Copyright (c) <year> <copyright holders>

-

Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:

-

The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.

-

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

-
- -
- - -
- -
-
-
-
-
- -
- - -
-
- - diff --git a/build-tools/maven/lib/wagon-file-2.8.jar b/build-tools/maven/lib/wagon-file-2.8.jar deleted file mode 100644 index b4be898b59..0000000000 Binary files a/build-tools/maven/lib/wagon-file-2.8.jar and /dev/null differ diff --git a/build-tools/maven/lib/wagon-file.license b/build-tools/maven/lib/wagon-file.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/wagon-file.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/wagon-http-2.8-shaded.jar b/build-tools/maven/lib/wagon-http-2.8-shaded.jar deleted file mode 100644 index bd8ce065b1..0000000000 Binary files a/build-tools/maven/lib/wagon-http-2.8-shaded.jar and /dev/null differ diff --git a/build-tools/maven/lib/wagon-http-shared-2.8.jar b/build-tools/maven/lib/wagon-http-shared-2.8.jar deleted file mode 100644 index a7284cdede..0000000000 Binary files a/build-tools/maven/lib/wagon-http-shared-2.8.jar and /dev/null differ diff --git a/build-tools/maven/lib/wagon-http-shared.license b/build-tools/maven/lib/wagon-http-shared.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/wagon-http-shared.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/wagon-http.license b/build-tools/maven/lib/wagon-http.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/wagon-http.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/build-tools/maven/lib/wagon-provider-api-2.8.jar b/build-tools/maven/lib/wagon-provider-api-2.8.jar deleted file mode 100644 index 86f55059b5..0000000000 Binary files a/build-tools/maven/lib/wagon-provider-api-2.8.jar and /dev/null differ diff --git a/build-tools/maven/lib/wagon-provider-api.license b/build-tools/maven/lib/wagon-provider-api.license deleted file mode 100644 index d645695673..0000000000 --- a/build-tools/maven/lib/wagon-provider-api.license +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cas-server-webapp/pom.xml b/cas-server-webapp/pom.xml deleted file mode 100644 index 12cd2911df..0000000000 --- a/cas-server-webapp/pom.xml +++ /dev/null @@ -1,310 +0,0 @@ - - - - 4.0.0 - - org.georchestra - root - 15.12-SNAPSHOT - - cas-server-webapp - war - Jasig CAS Web Application - - 3.2.6.RELEASE - 4.0.0 - - - - org.jasig.cas - cas-server-webapp-support - ${cas.version} - compile - - - org.springframework - spring-asm - - - - - org.jasig.cas - cas-server-support-ldap - ${cas.version} - compile - - - org.jasig.cas - cas-server-integration-memcached - ${cas.version} - - - org.springframework - spring-expression - ${spring.version} - runtime - - - org.springframework - spring-jdbc - ${spring.version} - runtime - - - org.springframework - spring-tx - ${spring.version} - runtime - - - org.springframework - spring-web - ${spring.version} - runtime - - - org.springframework - spring-webmvc - ${spring.version} - runtime - - - javax.servlet - jstl - 1.1.2 - jar - runtime - - - taglibs - standard - 1.1.2 - jar - runtime - - - - org.georchestra - georchestra-commons - ${project.version} - - - org.json - json - 20080701 - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - org.codehaus.groovy.maven - gmaven-plugin - - - org.georchestra - config - ${project.version} - ${server} - - - - - org.apache.maven.plugins - maven-war-plugin - - cas - - ${confdir}/${project.artifactId}/maven.filter - - - - true - ${confdir}/${project.artifactId} - - README - maven.filter - - - - true - ${basedir}/src/main/filtered-resources - - - - - - - org.eclipse.jetty - jetty-maven-plugin - 9.2.11.v20150529 - - 5 - - /cas - - - 8181 - - - - - maven-antrun-plugin - - - serverConfigCopy - generate-sources - - - - - - - - - - - - - - - - - - - - run - - - - - - - - - debianPackage - - - - org.apache.maven.plugins - maven-war-plugin - 2.1.1 - - generic - - - - maven-resources-plugin - 2.3 - - - copy-deb-resources - process-resources - copy-resources - - true - ${basedir}/target/deb - - - src/deb/resources - - - - - - - - net.sf.debian-maven - debian-maven-plugin - 1.0.6 - - georchestra-cas - geOrchestra CAS server - geOrchestra - PSC - psc@georchestra.org - true - - - - - - - rpmPackage - - - - org.apache.maven.plugins - maven-war-plugin - 2.1.1 - - generic - - - - org.codehaus.mojo - rpm-maven-plugin - 2.1.3 - - - generate-rpm - - rpm - - - - - georchestra-cas - UTF-8 - Applications/Internet - ${rpm.gpg.key} - - - /usr/share/lib/georchestra-cas - - - ${project.build.directory} - - cas-generic.war - - - - - - / - - - ${basedir}/src/deb/resources - - - - - - - - - - - - diff --git a/cas-server-webapp/src/deb/resources/etc/georchestra/cas/cas.properties b/cas-server-webapp/src/deb/resources/etc/georchestra/cas/cas.properties deleted file mode 100644 index 9578552cc5..0000000000 --- a/cas-server-webapp/src/deb/resources/etc/georchestra/cas/cas.properties +++ /dev/null @@ -1,38 +0,0 @@ -server.name=http://georchestra.mydomain.org -server.prefix=https://georchestra.mydomain.org/cas -instance.name=geOrchestra -# IP address or CIDR subnet allowed to access the /status URI of CAS that exposes health check information -cas.securityContext.status.allowedSubnet=127.0.0.1 - -ldapadmin.contextpath=/ldapadmin - -cas.themeResolver.defaultThemeName=cas-theme-default -cas.viewResolver.basename=default_views - -## -# Unique CAS node name -# host.name is used to generate unique Service Ticket IDs and SAMLArtifacts. This is usually set to the specific -# hostname of the machine running the CAS node, but it could be any label so long as it is unique in the cluster. -host.name=georchestra.mydomain.org - -ldap.url=ldap://127.0.1.1:389 -ldap.authn.groupSearchBaseDn=ou=groups,dc=georchestra,dc=org -ldap.authn.userSearchBaseDn=ou=users,dc=georchestra,dc=org -ldap.authn.searchFilter=(uid={user}) -ldap.admin.username=cn=admin,dc=georchestra,dc=org -ldap.admin.password=secret -ldap.authn.groupSearchFilter=(member=uid={1},ou=users,dc=georchestra,dc=org) -ldap.authn.groupRoleAttribute=cn -ldap.authn.pendingGroupName=PENDING - -# See http://www.ldaptive.org/ for information about ldap parameters -ldap.connectTimeout=30000 -ldap.useStartTLS=false -ldap.pool.minSize=3 -ldap.pool.maxSize=10 -ldap.pool.validateOnCheckout=false -ldap.pool.validatePeriodically=false -ldap.pool.validatePeriod=1800 -ldap.pool.prunePeriod=300 -ldap.pool.idleTime=600 -ldap.pool.blockWaitTime=0 diff --git a/cas-server-webapp/src/deb/resources/etc/georchestra/cas/log4j/log4j.xml b/cas-server-webapp/src/deb/resources/etc/georchestra/cas/log4j/log4j.xml deleted file mode 100644 index d8643b73ec..0000000000 --- a/cas-server-webapp/src/deb/resources/etc/georchestra/cas/log4j/log4j.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/filtered-resources/WEB-INF/cas.properties b/cas-server-webapp/src/main/filtered-resources/WEB-INF/cas.properties deleted file mode 100644 index 78de29dafd..0000000000 --- a/cas-server-webapp/src/main/filtered-resources/WEB-INF/cas.properties +++ /dev/null @@ -1,56 +0,0 @@ -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -server.name=http://${host}:${port} -server.prefix=${server.name}/cas -# IP address or CIDR subnet allowed to access the /status URI of CAS that exposes health check information -cas.securityContext.status.allowedSubnet=127.0.0.1 - - -cas.themeResolver.defaultThemeName=cas-theme-default -cas.viewResolver.basename=default_views - -## -# Unique CAS node name -# host.name is used to generate unique Service Ticket IDs and SAMLArtifacts. This is usually set to the specific -# hostname of the machine running the CAS node, but it could be any label so long as it is unique in the cluster. -host.name=${host} - -ldap.url=${ldapUrl} -ldap.authn.groupSearchBaseDn=${groupSearchBaseDn} -ldap.authn.userSearchBaseDn=${userSearchBaseDn} -ldap.authn.searchFilter=${userFilter} -ldap.admin.username=${ldapAdminUsername} -ldap.admin.password=${ldapAdminPassword} -ldap.authn.groupSearchFilter=${ldapGroupSearchFilter} -ldap.authn.groupRoleAttribute=${groupRoleAttribute} -ldap.authn.pendingGroupName=${ldapPendingGroupName} - -# See http://www.ldaptive.org/ for information about ldap parameters -ldap.connectTimeout=30000 -ldap.useStartTLS=false -ldap.pool.minSize=3 -ldap.pool.maxSize=10 -ldap.pool.validateOnCheckout=false -ldap.pool.validatePeriodically=false -ldap.pool.validatePeriod=1800 -ldap.pool.prunePeriod=300 -ldap.pool.idleTime=600 -ldap.pool.blockWaitTime=0 - diff --git a/cas-server-webapp/src/main/java/org/georchestra/cas/ldap/GeorchestraLdapAuthenticationHandler.java b/cas-server-webapp/src/main/java/org/georchestra/cas/ldap/GeorchestraLdapAuthenticationHandler.java deleted file mode 100644 index 2a5676f726..0000000000 --- a/cas-server-webapp/src/main/java/org/georchestra/cas/ldap/GeorchestraLdapAuthenticationHandler.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.georchestra.cas.ldap; - -import java.security.GeneralSecurityException; -import java.util.Collection; - -import javax.security.auth.login.AccountException; -import javax.validation.constraints.NotNull; - -import org.jasig.cas.authentication.HandlerResult; -import org.jasig.cas.authentication.LdapAuthenticationHandler; -import org.jasig.cas.authentication.PreventedException; -import org.jasig.cas.authentication.UsernamePasswordCredential; -import org.ldaptive.BindRequest; -import org.ldaptive.Connection; -import org.ldaptive.Credential; -import org.ldaptive.DefaultConnectionFactory; -import org.ldaptive.LdapEntry; -import org.ldaptive.LdapException; -import org.ldaptive.SearchOperation; -import org.ldaptive.SearchRequest; -import org.ldaptive.SearchResult; -import org.ldaptive.auth.Authenticator; - -/** - * Extends Ldap authentication handler by checking whether the user is pending or valid. - * - * @author Jesse on 6/26/2014. - */ -public class GeorchestraLdapAuthenticationHandler extends LdapAuthenticationHandler { - - private String adminUser; - private String adminPassword; - private String baseDn; - private String groupSearchFilter; - private String groupRoleAttribute; - private String pendingGroupName; - - private DefaultConnectionFactory connectionFactory; - - public void setAdminUser(String adminUser) { - this.adminUser = adminUser; - } - - public void setAdminPassword(String adminPassword) { - this.adminPassword = adminPassword; - } - - public void setBaseDn(String baseDn) { - this.baseDn = baseDn; - } - - public void setGroupSearchFilter(String groupSearchFilter) { - this.groupSearchFilter = groupSearchFilter; - } - - public void setGroupRoleAttribute(String groupRoleAttribute) { - this.groupRoleAttribute = groupRoleAttribute; - } - - public void setPendingGroupName(String pendingGroupName) { - this.pendingGroupName = pendingGroupName; - } - /** - * Creates a new authentication handler that delegates to the given authenticator. - * - * @param authenticator Ldaptive authenticator component. - */ - public GeorchestraLdapAuthenticationHandler(@NotNull Authenticator authenticator, - @NotNull String adminUser, - @NotNull String adminPassword, - @NotNull String baseDn, - @NotNull String groupSearchFilter, - @NotNull String groupRoleAttribute, - @NotNull String pendingGroupName) { - super(authenticator); - this.adminUser = adminUser; - this.adminPassword = adminPassword; - this.baseDn = baseDn; - this.groupSearchFilter = groupSearchFilter; - this.groupRoleAttribute = groupRoleAttribute; - this.pendingGroupName = pendingGroupName; - } - - @Override - protected HandlerResult authenticateUsernamePasswordInternal(UsernamePasswordCredential upc) - throws GeneralSecurityException, PreventedException { - final HandlerResult handlerResult = super.authenticateUsernamePasswordInternal(upc); - - final Connection conn = this.connectionFactory.getConnection(); - try { - BindRequest bindRequest = new BindRequest(adminUser, new Credential(adminPassword)); - conn.open(bindRequest); - - SearchOperation search = new SearchOperation(conn); - final String searchFilter = this.groupSearchFilter.replace("{1}", upc.getUsername()); - SearchResult result = search.execute( - new SearchRequest(this.baseDn, searchFilter, this.groupRoleAttribute)).getResult(); - - if (result.getEntries().isEmpty()) { - throw new AccountException("User is not part of any groups."); - } - for (LdapEntry entry : result.getEntries()) { - final Collection groupNames = entry.getAttribute(this.groupRoleAttribute).getStringValues(); - for (String name : groupNames) { - if (name.equals(this.pendingGroupName)) { - throw new AccountException("User is still a pending user."); - } - } - } - } catch (LdapException e) { - throw new PreventedException("Unexpected LDAP error", e); - } finally { - conn.close(); - } - - return handlerResult; - } - - public void setConnectionFactory(DefaultConnectionFactory connectionFactory) { - this.connectionFactory = connectionFactory; - } -} diff --git a/cas-server-webapp/src/main/resources/cas-theme-default.properties b/cas-server-webapp/src/main/resources/cas-theme-default.properties deleted file mode 100644 index 807cd88e85..0000000000 --- a/cas-server-webapp/src/main/resources/cas-theme-default.properties +++ /dev/null @@ -1,21 +0,0 @@ -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -standard.custom.css.file=/css/cas.css -cas.javascript.file=/js/cas.js diff --git a/cas-server-webapp/src/main/resources/default_views.properties b/cas-server-webapp/src/main/resources/default_views.properties deleted file mode 100644 index e28fe4cec8..0000000000 --- a/cas-server-webapp/src/main/resources/default_views.properties +++ /dev/null @@ -1,75 +0,0 @@ -### Login view (/login) - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -casLoginView.(class)=org.springframework.web.servlet.view.JstlView -casLoginView.url=/WEB-INF/view/jsp/default/ui/casLoginView.jsp - -### Display login (warning) messages -casLoginMessageView.(class)=org.springframework.web.servlet.view.JstlView -casLoginMessageView.url=/WEB-INF/view/jsp/default/ui/casLoginMessageView.jsp - -### Login confirmation view (logged in, warn=true) -casLoginConfirmView.(class)=org.springframework.web.servlet.view.JstlView -casLoginConfirmView.url=/WEB-INF/view/jsp/default/ui/casConfirmView.jsp - -### Logged-in view (logged in, no service provided) -casLoginGenericSuccessView.(class)=org.springframework.web.servlet.view.JstlView -casLoginGenericSuccessView.url=/WEB-INF/view/jsp/default/ui/casGenericSuccess.jsp - -### Logout view (/logout) -casLogoutView.(class)=org.springframework.web.servlet.view.JstlView -casLogoutView.url=/WEB-INF/view/jsp/default/ui/casLogoutView.jsp - -### CAS error view -viewServiceErrorView.(class)=org.springframework.web.servlet.view.JstlView -viewServiceErrorView.url=/WEB-INF/view/jsp/default/ui/serviceErrorView.jsp - -viewServiceSsoErrorView.(class)=org.springframework.web.servlet.view.JstlView -viewServiceSsoErrorView.url=/WEB-INF/view/jsp/default/ui/serviceErrorSsoView.jsp - -### CAS statistics view -viewStatisticsView.(class)=org.springframework.web.servlet.view.JstlView -viewStatisticsView.url=/WEB-INF/view/jsp/monitoring/viewStatistics.jsp - -### Expired Password Error message -casExpiredPassView.(class)=org.springframework.web.servlet.view.JstlView -casExpiredPassView.url=/WEB-INF/view/jsp/default/ui/casExpiredPassView.jsp - -### Locked Account Error message -casAccountLockedView.(class)=org.springframework.web.servlet.view.JstlView -casAccountLockedView.url=/WEB-INF/view/jsp/default/ui/casAccountLockedView.jsp - -### Disabled Account Error message -casAccountDisabledView.(class)=org.springframework.web.servlet.view.JstlView -casAccountDisabledView.url=/WEB-INF/view/jsp/default/ui/casAccountDisabledView.jsp - -### Must Change Password Error message -casMustChangePassView.(class)=org.springframework.web.servlet.view.JstlView -casMustChangePassView.url=/WEB-INF/view/jsp/default/ui/casMustChangePassView.jsp - -### Bad Hours Error message -casBadHoursView.(class)=org.springframework.web.servlet.view.JstlView -casBadHoursView.url=/WEB-INF/view/jsp/default/ui/casBadHoursView.jsp - -### Bad Workstation Error message -casBadWorkstationView.(class)=org.springframework.web.servlet.view.JstlView -casBadWorkstationView.url=/WEB-INF/view/jsp/default/ui/casBadWorkstationView.jsp - diff --git a/cas-server-webapp/src/main/resources/messages.properties b/cas-server-webapp/src/main/resources/messages.properties deleted file mode 100644 index 1c81e4015d..0000000000 --- a/cas-server-webapp/src/main/resources/messages.properties +++ /dev/null @@ -1,112 +0,0 @@ -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Congratulations on bringing CAS online! To learn how to authenticate, please review the default authentication handler configuration. -screen.welcome.security=For security reasons, please Log Out and Exit your web browser when you are done accessing services that require authentication! -screen.welcome.redirecting=Redirecting you to the home page in a second... -screen.welcome.instructions=Enter your Username and Password -screen.welcome.label.netid=Username: -screen.welcome.label.netid.accesskey=u -screen.welcome.label.password=Password: -screen.welcome.label.password.accesskey=p -screen.welcome.label.warn=Warn me before logging me into other sites. -screen.welcome.label.warn.accesskey=w -screen.welcome.button.login=LOGIN -screen.welcome.button.clear=CLEAR -screen.welcome.or=OR -screen.welcome.link.forgot=forgot password -screen.welcome.link.newaccount=Create an account - -logo.title=go to Jasig home page -copyright=Copyright © 2005–2012 Jasig, Inc. All rights reserved. - -# Blocked Errors Page -screen.blocked.header=Access Denied -screen.blocked.message=You've entered the wrong password for the user too many times. You've been throttled. - -#Confirmation Screen Messages -screen.confirmation.message=Click here to go to the application. - -#Generic Success Screen Messages -screen.success.header=Log In Successful -screen.success.success=You have successfully logged into the Central Authentication Service. -screen.success.security=When you are finished, for security reasons, please Log Out and Exit your web browser. - -#Logout Screen Messages -screen.logout.header=Logout successful -screen.logout.success=You have successfully logged out of the Central Authentication Service. -screen.logout.security=For security reasons, exit your web browser. -screen.logout.redirect=The service from which you arrived has supplied a link you may follow by clicking here. - -screen.service.sso.error.header=Re-Authentication Required to Access this Service -screen.service.sso.error.message=You attempted to access a service that requires authentication without re-authenticating. Please try authenticating again. - -error.invalid.loginticket=You cannot attempt to re-submit a form that has been submitted already. -required.username=Username is a required field. -required.password=Password is a required field. - -# Authentication failure messages -authenticationFailure.AccountDisabledException=This account has been disabled. -authenticationFailure.AccountLockedException=This account has been locked. -authenticationFailure.CredentialExpiredException=Your password has expired. -authenticationFailure.InvalidLoginLocationException=You cannot login from this workstation. -authenticationFailure.InvalidLoginTimeException=Your account is forbidden to login at this time. -authenticationFailure.AccountNotFoundException=Invalid credentials. -authenticationFailure.FailedLoginException=Invalid credentials. -authenticationFailure.UNKNOWN=Invalid credentials. - -INVALID_REQUEST_PROXY='pgt' and 'targetService' parameters are both required -INVALID_TICKET_SPEC=Ticket failed validation specification. Possible errors could include attempting to validate a Proxy Ticket via a Service Ticket validator, or not complying with the renew true request. -INVALID_REQUEST='service' and 'ticket' parameters are both required -INVALID_TICKET=Ticket ''{0}'' not recognized -INVALID_SERVICE=Ticket ''{0}'' does not match supplied service. The original service was ''{1}'' and the supplied service was ''{2}''. -INVALID_PROXY_CALLBACK=The supplied proxy callback url ''{0}'' could not be authenticated. -UNAUTHORIZED_SERVICE_PROXY=The supplied service ''{0}'' is not authorized to use CAS proxy authentication. - -screen.service.error.header=Application Not Authorized to Use CAS -screen.service.error.message=The application you attempted to authenticate to is not authorized to use CAS. -screen.service.empty.error.message=The services registry of CAS is empty and has no service definitions. \ -Applications that wish to authenticate with CAS must explicitly be defined in the services registry. - -# Password policy -password.expiration.warning=Your password expires in {0} day(s). Please change your password now. -password.expiration.loginsRemaining=You have {0} login(s) remaining before you MUST change your password. -screen.accountdisabled.heading=This account has been disabled. -screen.accountdisabled.message=Please contact the system administrator to regain access. -screen.accountlocked.heading=This account has been locked. -screen.accountlocked.message=Please contact the system administrator to regain access. -screen.expiredpass.heading=Your password has expired. -screen.expiredpass.message=Please change your password. -screen.mustchangepass.heading=You must change your password. -screen.mustchangepass.message=Please change your password. -screen.badhours.heading=Your account is forbidden to login at this time. -screen.badhours.message=Please try again later. -screen.badworkstation.heading=You cannot login from this workstation. -screen.badworkstation.message=Please contact the system administrator to regain access. - -# OAuth -screen.oauth.confirm.header=Authorization -screen.oauth.confirm.message=Do you want to grant access to your complete profile to "{0}" ? -screen.oauth.confirm.allow=Allow - -# Unavailable -screen.unavailable.heading=CAS is Unavailable -screen.unavailable.message=There was an error trying to complete your request. Please notify your support desk or try again. diff --git a/cas-server-webapp/src/main/resources/messages_ar.properties b/cas-server-webapp/src/main/resources/messages_ar.properties deleted file mode 100644 index cd272adbb2..0000000000 --- a/cas-server-webapp/src/main/resources/messages_ar.properties +++ /dev/null @@ -1,71 +0,0 @@ -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=\u062A\u0647\u0627\u0646\u064A\u0646\u0627 \u0639\u0644\u0649 \u062C\u0644\u0628 CAS \u0639\u0644\u0649 \u0627\u0644\u0627\u0646\u062A\u0631\u0646\u062A! -screen.welcome.security=\u0644\u0623\u0633\u0628\u0627\u0628 \u0623\u0645\u0646\u064A\u0629\u060C \u0627\u0644\u0631\u062C\u0627\u0621 \u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062E\u0631\u0648\u062C \u0648\u062E\u0631\u0648\u062C \u0645\u062A\u0635\u0641\u062D \u0627\u0644\u0648\u064A\u0628 \u0627\u0644\u062E\u0627\u0635 \u0628\u0643 \u0628\u0639\u062F \u0627\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0627\u0644\u062E\u062F\u0645\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u062A\u0637\u0644\u0628 \u0627\u0644\u0645\u0635\u0627\u062F\u0642\u0629! -screen.welcome.instructions=\u0623\u062F\u062E\u0644 \u0627\u0633\u0645 \u0627\u0644\u0645\u0633\u062A\u062E\u062F\u0645 \u0648\u0643\u0644\u0645\u0629 \u0627\u0644\u0633\u0631 -screen.welcome.label.netid=\u0627\u0633\u0645 \u0627\u0644\u0645\u0633\u062A\u062E\u062F\u0645 -screen.welcome.label.netid.accesskey= -screen.welcome.label.password=\u0643\u0644\u0645\u0629 \u0627\u0644\u0633\u0631 -screen.welcome.label.password.accesskey= -screen.welcome.label.warn=\u062A\u062D\u0630\u0631\u0646\u064A \u0642\u0628\u0644 \u062A\u0633\u062C\u064A\u0644\u064A \u0641\u064A \u0627\u0644\u0645\u0648\u0627\u0642\u0639 \u0627\u0644\u0623\u062E\u0631\u0649. -screen.welcome.label.warn.accesskey= -screen.welcome.button.login=\u062F\u062E\u0648\u0644 -screen.welcome.button.clear=\u0627\u0644\u063A\u0627 - -logo.title=\u0627\u0644\u0630\u0647\u0627\u0628 \u0625\u0644\u0649 \u0627\u0644\u0635\u0641\u062D\u0629 Jasig -copyright=\u062D\u0642 \u0627\u0644\u0646\u0634\u0631 © 2005 - 2012 Jasig, Inc. \u062C\u0645\u064A\u0639 \u0627\u0644\u062D\u0642\u0648\u0642 \u0645\u062D\u0641\u0648\u0638\u0629. - -# Blocked Errors Page -screen.blocked.header=\u0627\u0644\u0648\u0635\u0648\u0644 \u0645\u0631\u0641\u0648\u0636 -screen.blocked.message=\u0644\u0642\u062F \u0642\u0645\u062A \u0628\u0625\u062F\u062E\u0627\u0644 \u0643\u0644\u0645\u0629 \u0645\u0631\u0648\u0631 \u062E\u0627\u0637\u0626\u0629 \u0644\u0644\u0645\u0633\u062A\u062E\u062F\u0645 \u0645\u0631\u0627\u062A \u0643\u062B\u064A\u0631\u0629 \u062C\u062F\u0627. \u0644\u0642\u062F \u0643\u0646\u062A \u0645\u062E\u0646\u0648\u0642. - -#Confirmation Screen Messages -screen.confirmation.message=\u0627\u0636\u063A\u0637 \u0647\u0646\u0627 {0} \u0644\u0644\u0630\u0647\u0627\u0628 \u0625\u0644\u0649 \u0627\u0644\u062A\u0637\u0628\u064A\u0642 - -#Generic Success Screen Messages -screen.success.header=\u062A\u0633\u062C\u064A\u0644 \u0646\u0627\u062C\u062D -screen.success.success=\u0644\u0642\u062F \u0642\u0645\u062A \u0628\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062F\u062E\u0648\u0644 \u0628\u0646\u062C\u0627\u062D \u0644\u0644\u062F\u062E\u0648\u0644 \u0625\u0644\u0649 \u0645\u0631\u0643\u0632 \u0627\u0644\u0645\u0635\u0627\u062F\u0642\u0629 -screen.success.security=\u0644\u0623\u0633\u0628\u0627\u0628 \u0623\u0645\u0646\u064A\u0629\u060C \u0627\u0644\u0631\u062C\u0627\u0621 \u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062E\u0631\u0648\u062C \u0648 \u0627\u0644\u062E\u0631\u0648\u062C \u0645\u0646 \u0645\u062A\u0635\u0641\u062D \u0627\u0644\u0648\u064A\u0628 \u0627\u0644\u062E\u0627\u0635 \u0628\u0643 \u0628\u0639\u062F \u0627\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0627\u0644\u062E\u062F\u0645\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u062A\u0637\u0644\u0628 \u0627\u0644\u0645\u0635\u0627\u062F\u0642\u0629! - -#Logout Screen Messages -screen.logout.header=\u062E\u0631\u0648\u062C \u0646\u0627\u062C\u062D -screen.logout.success=\u0644\u0642\u062F \u0642\u0645\u062A \u0628\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062E\u0631\u0648\u062C \u0628\u0646\u062C\u0627\u062D \u0644\u0644\u062E\u0631\u0648\u062C \u0645\u0646 \u0645\u0631\u0643\u0632 \u0627\u0644\u0645\u0635\u0627\u062F\u0642\u0629 -screen.logout.security=\u0644\u0623\u0633\u0628\u0627\u0628 \u0623\u0645\u0646\u064A\u0629\u060C \u064A\u062C\u0628 \u0627\u0644\u062E\u0631\u0648\u062C \u0645\u0646 \u0645\u062A\u0635\u0641\u062D \u0627\u0644\u0648\u064A\u0628 \u0627\u0644\u062E\u0627\u0635 \u0628\u0643 -screen.logout.redirect=\u0627\u0644\u0645\u0631\u0643\u0632 \u0627\u0644\u0630\u064A \u0648\u0635\u0644\u062A \u0645\u0646\u0647 \u0642\u062F \u0632\u0648\u062F \u0648\u0635\u0644 \u0627\u0631\u062A\u0628\u0627\u0637 \u0627\u062A\u0628\u0639 \u0628\u0627\u0644\u0636\u063A\u0637 \u0647\u0646\u0627 - -screen.service.sso.error.header= \u0644\u0644\u0648\u0635\u0648\u0644 \u0625\u0644\u0649 \u0647\u0630\u0627 \u0627\u0644\u0645\u0631\u0643\u0632 \u064A\u062C\u0628 \u0625\u0639\u0627\u062F\u0629 \u0627\u0644\u0645\u0635\u0627\u062F\u0642\u0629 -screen.service.sso.error.message=\u0644\u0642\u062F \u062D\u0627\u0648\u0644\u062A \u0627\u0644\u0648\u0635\u0648\u0644 \u0625\u0644\u0649 \u062E\u062F\u0645\u0629 \u064A\u062A\u0637\u0644\u0628 \u0645\u0635\u0627\u062F\u0642\u0629 \u0645\u0646 \u062F\u0648\u0646 \u0645\u0635\u0627\u062F\u0642\u0629 \u0645\u0646 \u062C\u062F\u064A\u062F.\u0627\u0644\u0631\u062C\u0627\u0621 \u062D\u0627\u0648\u0644 \u0644\u0645\u0635\u0627\u062F\u0642\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 {0} - -error.invalid.loginticket=\u0644\u0627 \u064A\u0645\u0643\u0646\u0643 \u0645\u062D\u0627\u0648\u0644\u0629 \u0625\u0639\u0627\u062F\u0629 \u0627\u0644\u062A\u0642\u062F\u064A\u0645 \u0644\u0644\u0646\u0645\u0648\u0630\u062C \u0627\u0644\u0630\u064A \u062A\u0645 \u062A\u0642\u062F\u064A\u0645\u0647 \u0628\u0627\u0644\u0641\u0639\u0644 -required.username=\u0627\u0633\u0645 \u0627\u0644\u0645\u0633\u062A\u062E\u062F\u0645 \u0647\u0648 \u0627\u0644\u062D\u0642\u0644 \u0627\u0644\u0645\u0637\u0644\u0648\u0628 -required.password=\u0643\u0644\u0645\u0629 \u0627\u0644\u0633\u0631 \u0647\u064A \u0627\u0644\u062D\u0642\u0644 \u0627\u0644\u0645\u0637\u0644\u0648\u0628 -error.authentication.credentials.bad= \u0623\u0648\u0631\u0627\u0642 \u0627\u0644\u0627\u0639\u062A\u0645\u0627\u062F \u0627\u0644\u062A\u064A \u0642\u062F\u0645\u062A\u0647\u0627 \u0644\u0627 \u064A\u0645\u0643\u0646 \u062A\u062D\u062F\u064A\u0647 \u0644\u0644\u0645\u0635\u0627\u062F\u0642\u0629 \u0639\u0644\u064A\u0647 -error.authentication.credentials.unsupported= \u0623\u0648\u0631\u0627\u0642 \u0627\u0644\u0627\u0639\u062A\u0645\u0627\u062F \u0627\u0644\u062A\u064A \u0642\u062F\u0645\u062A\u0647\u0627 \u063A\u064A\u0631 \u0645\u0639\u062A\u0645\u062F\u0629 \u0628\u0648\u0627\u0633\u0637\u0629 - -INVALID_REQUEST_PROXY=pgt \u0648 targetService \u0645\u0639\u0644\u0645\u0627\u062A \u06A9\u0644\u0627\u0647\u0645\u0627 \u0645\u0637\u0644\u0648\u0628 -INVALID_TICKET_SPEC=\u0641\u0634\u0644 \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646 \u0635\u062D\u0629 \u0645\u0648\u0627\u0635\u0641\u0627\u062A \u0627\u0644\u062A\u0630\u0627\u0643\u0631. \u064A\u0645\u0643\u0646 \u0623\u0646 \u062A\u062A\u0636\u0645\u0646 \u0623\u062E\u0637\u0627\u0621 \u0645\u062D\u062A\u0645\u0644\u0629 \u062A\u062D\u0627\u0648\u0644 \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646 \u0635\u062D\u0629 \u0627\u0644\u062A\u0630\u0627\u0643\u0631 \u0639\u0646 \u0637\u0631\u064A\u0642 \u0648\u0643\u064A\u0644 \u0645\u062F\u0642\u0642 \u062A\u0630\u0643\u0631\u0629 \u0627\u0644\u062E\u062F\u0645\u0629\u060C \u0623\u0648 \u0644\u0645 \u064A\u0645\u062A\u062B\u0644 \u0644\u0637\u0644\u0628 \u062A\u062C\u062F\u064A\u062F \u062D\u0642\u064A\u0642\u064A -INVALID_REQUEST=service \u0648 ticket \u0645\u0639\u0644\u0645\u0627\u062A \u06A9\u0644\u0627\u0647\u0645\u0627 \u0645\u0637\u0644\u0648\u0628 -INVALID_TICKET=\u062A\u0630\u0643\u0631\u0629 {0} \u0644\u0627 \u064A\u0639\u062A\u0631\u0641 -INVALID_SERVICE=\u062A\u0630\u0643\u0631\u0629 {0} \u0644\u0627 \u064A\u062A\u0637\u0627\u0628\u0642 \u0645\u0639 \u0627\u0644\u062E\u062F\u0645\u0629 \u0627\u0644\u0645\u0642\u062F\u0645\u0629. \u0643\u0627\u0646\u062A \u0627\u0644\u062E\u062F\u0645\u0629 \u0627\u0644\u0623\u0635\u0644\u064A\u0629 {1} \u0648\u06A9\u0627\u0646\u062A \u0627\u0644\u062E\u062F\u0645\u0629 \u0627\u0644\u0645\u0642\u062F\u0645\u0629 {2{ - -screen.service.error.header=\u0627\u0644\u062A\u0637\u0628\u064A\u0642 \u0644\u0627 \u064A\u0633\u0645\u062D \u0627\u0633\u062A\u062E\u062F\u0627\u0645 CAS -screen.service.error.message=\u0627\u0644\u062E\u062F\u0645\u0629 \u0627\u0644\u062A\u064A \u062A\u0637\u0644\u0628\u064A\u0646\u0647\u0627 \u063A\u0631 \u0645\u0633\u0645\u0648\u062D\u0629 \u0628\u0647\u0627 \u0644\u062F\u0649 \u0627\u0633\u062A\u0639\u0645\u0627\u0644 CAS diff --git a/cas-server-webapp/src/main/resources/messages_ca.properties b/cas-server-webapp/src/main/resources/messages_ca.properties deleted file mode 100644 index d9375a86fb..0000000000 --- a/cas-server-webapp/src/main/resources/messages_ca.properties +++ /dev/null @@ -1,110 +0,0 @@ -#Author: Evili del Rio i Silvan and Alex Henrie - -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Felicitats per engegar el CAS correctament! Per aprendre com autenticar, si us plau, repasseu la configuraci\u00f3 del gestor d'autenticaci\u00f3 per defecte. -screen.welcome.security=Per raons de seguretat, si us plau, tanqueu la sessi\u00f3 i el vostre navegador web quan h\u00e0giu acabat d'accedir als serveis que requereixen autenticaci\u00f3. -screen.welcome.instructions=Introdu\u00efu el vostre nom d'usuari i contrasenya. -screen.welcome.label.netid=Nom d'usuari: -screen.welcome.label.netid.accesskey=u -screen.welcome.label.password=Contrasenya: -screen.welcome.label.password.accesskey=c -screen.welcome.label.warn=Aviseu-me abans d'obrir sessi\u00f3 en altres llocs. -screen.welcome.label.warn.accesskey=a -screen.welcome.button.login=INICIA SESSI\u00d3 -screen.welcome.button.clear=NETEJA - -logo.title=v\u00e9s a la p\u00e0gina principal de Jasig -copyright=Copyright © 2005–2012 Jasig, Inc. Tots els drets reservats. - -# Blocked Errors Page -screen.blocked.header=Acc\u00e9s denegat -screen.blocked.message=Heu introdu\u00efda una contrasenya equivocada pel usuari massa vegades. Se us ha restringit. - -#Confirmation Screen Messages -screen.confirmation.message=Feu clic aqu\u00ed per a anar a l'aplicaci\u00f3. - -#Generic Success Screen Messages -screen.success.header=Inici de sessi\u00f3 reeixit. -screen.success.success=Heu iniciada amb \u00e8xit la sessi\u00f3 al Servei Central d'Autenticaci\u00f3 (CAS). -screen.success.security=Per raons de seguretat, si us plau, tanqueu la sessi\u00f3 i el vostre navegador web quan hagi acabat d'accedir als serveis que requereixen autenticaci\u00f3. - -#Logout Screen Messages -screen.logout.header=Tancament de sessi\u00f3 reeixit. -screen.logout.success=Heu tancada amb \u00e8xit la sessi\u00f3 al Servei Central d'Autenticaci\u00f3 (CAS). -screen.logout.security=Per raons de seguretat, tanqueu el vostre navegador web. -screen.logout.redirect=El servei des del qual heu arribat ha proporcionat un enlla\u00e7 que podeu seguir per fer clic aqu\u00ed. - -screen.service.sso.error.header=Cal reautenticar per a accedir a aquest servei -screen.service.sso.error.message=Heu intentat accedir a un servei que requereix autenticaci\u00f3 sense reautenticar. Si us plau, intenteu autenticar de nou. - -error.invalid.loginticket=No podeu intentar reenviar un formulari que ja s'ha enviat. -required.username=El nom d'usuari \u00e9s un camp obligatori. -required.password=La contrasenya \u00e9s un camp obligatori. - -# Authentication failure messages -authenticationFailure.AccountDisabledException=S'ha deshabilitat aquest compte. -authenticationFailure.AccountLockedException=S'ha bloquejat aquest compte. -authenticationFailure.CredentialExpiredException=La vostra contrasenya ha caducada. -authenticationFailure.InvalidLoginLocationException=No podeu iniciar sessi\u00f3 des d'aquesta estaci\u00f3 de treball. -authenticationFailure.InvalidLoginTimeException=Est\u00e0 prohibit iniciar sessi\u00f3 amb el vostre compte en aquest moment. -authenticationFailure.AccountNotFoundException=Credencials inv\u00e0lids. -authenticationFailure.FailedLoginException=Credencials inv\u00e0lids. -authenticationFailure.UNKNOWN=Credencials inv\u00e0lids. - -INVALID_REQUEST_PROXY=calen ambd\u00f3s dels par\u00e0metres 'pgt' i 'targetService' -INVALID_TICKET_SPEC=El tiquet ha fallada l'especificaci\u00f3 de validaci\u00f3. Els errors possibles poden incloure intentar validar un tiquet de proxy mitjan\u00e7ant un validador de tiquets de servei, o no complir amb la petici\u00f3 de renovaci\u00f3 (renew true). -INVALID_REQUEST=calen ambd\u00f3s dels par\u00e0metres 'service' i 'ticket' -INVALID_TICKET=No s'ha reconegut el tiquet ''{0}'' -INVALID_SERVICE=El tiquet ''{0}'' no coincideix amb el servei proporcionat. El servei original era ''{1}'' i el servei proporcionat era ''{2}''. -INVALID_PROXY_CALLBACK=L'adre\u00e7a de retrotrucada de proxy prove\u00efda ''{0}'' no s'ha poguda autenticar. -UNAUTHORIZED_SERVICE_PROXY=El servei proporcionat ''{0}'' no est\u00e0 autoritzat a utilitzar l'autenticaci\u00f3 de proxy del CAS. - -screen.service.error.header=Aplicaci\u00f3 no autoritzada a utilitzar el CAS -screen.service.error.message=L'aplicaci\u00f3 a que heu intentat autenticar no est\u00e0 autoritzada a utilitzar el CAS. -screen.service.empty.error.message=El registre de serveis del CAS est\u00e0 buit i no t\u00e9 definicions de servei. \ -Les aplicacions que volen autenticar amb el CAS han de ser expl\u00edcitament definides en el registre de serveis. - -# Password policy -password.expiration.warning=La vostra contrasenya caduca en {0} dies. Si us plau, canvieu la vostra contrasenya ara. -password.expiration.loginsRemaining=Teniu {0} inicis de sessi\u00f3 restant abans que HEU de canviar la vostra contrasenya. -screen.accountdisabled.heading=S'ha deshabilitat aquest compte. -screen.accountdisabled.message=Si us plau, contacteu a l'administrador de sistema per a recobrar l'acc\u00e9s. -screen.accountlocked.heading=S'ha bloquejat aquest compte. -screen.accountlocked.message=Si us plau, contacteu a l'administrador de sistema per a recobrar l'acc\u00e9s. -screen.expiredpass.heading=La vostra contrasenya ha caducada. -screen.expiredpass.message=Si us plau, canvieu la vostra contrasenya. -screen.mustchangepass.heading=Has de canviar la vostra contrasenya. -screen.mustchangepass.message=Si us plau, canvieu la vostra contrasenya. -screen.badhours.heading=Est\u00e0 prohibit iniciar sessi\u00f3 amb el vostre compte en aquest moment. -screen.badhours.message=Si us plau, intenteu m\u00e9s tard. -screen.badworkstation.heading=No podeu iniciar sessi\u00f3 des d'aquesta estaci\u00f3 de treball. -screen.badworkstation.message=Si us plau, contacteu a l'administrador de sistema per a recobrar l'acc\u00e9s. - -# OAuth -screen.oauth.confirm.header=Autoritzaci\u00f3 -screen.oauth.confirm.message=Voleu concedir acc\u00e9s al vostre perfil complet a "{0}"? -screen.oauth.confirm.allow=Permet - -# Unavailable -screen.unavailable.heading=El CAS no est\u00e0 disponible -screen.unavailable.message=Ha hagu\u00e9s un error al intentar complir amb la vostra petici\u00f3. Si us plau, notifiqueu al vostre servei d'assist\u00e8ncia o intenteu de nou. diff --git a/cas-server-webapp/src/main/resources/messages_cs.properties b/cas-server-webapp/src/main/resources/messages_cs.properties deleted file mode 100644 index 80fdb073aa..0000000000 --- a/cas-server-webapp/src/main/resources/messages_cs.properties +++ /dev/null @@ -1,103 +0,0 @@ -#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net) -#Welcome Screen Messages - -INVALID_PROXY_CALLBACK = Poskytnut\u00E9 URL proxy callbacku ''{0}'' nelze autentifikovat. - -INVALID_REQUEST = Parametry 'service' a 'ticket' jsou povinn\u00E9 - -INVALID_REQUEST_PROXY = Parametry 'pgt' a 'targetService' jsou povinn\u00E9 - -INVALID_SERVICE = Ticket ''{0}'' nesouhlas\u00ED s poskytovanou slu\u017Ebou. P\u016Fvodn\u00ED slu\u017Eba byla ''{1}'', poskytnut\u00E1 je ''{2}''. - -INVALID_TICKET = Ticket ''{0}'' nebyl rozpozn\u00E1n - -INVALID_TICKET_SPEC = Ticket neporo\u0161el kontrolou validity. Mo\u017En\u00E9 chyby zahrnuj\u00ED pokus o ov\u011B\u0159en\u00ED Proxy Ticketu pomoc\u00ED ov\u011B\u0159en\u00ED Service Ticketu nebo nedodr\u017Een\u00ED po\u017Eadavku na renew. - -UNAUTHORIZED_SERVICE_PROXY = Poskytnut\u00E1 slu\u017Eba ''{0}'' nen\u00ED opr\u00E1vn\u011Bn\u00ED k pou\u017Eit\u00ED CAS proxy autentizace. - -authenticationFailure.AccountDisabledException = Tento \u00FA\u010Det byl zak\u00E1z\u00E1n. -authenticationFailure.AccountLockedException = Tento \u00FA\u010Det byl uzam\u010Den. -authenticationFailure.AccountNotFoundException = Nezn\u00E1m\u00E9 p\u0159ihla\u0161ovac\u00ED \u00FAdaje. -authenticationFailure.CredentialExpiredException = Va\u0161e heslo ji\u017E nen\u00ED platn\u00E9. -authenticationFailure.FailedLoginException = Neplatn\u00E9 p\u0159ihla\u0161ovac\u00ED \u00FAdaje. -authenticationFailure.InvalidLoginLocationException = Z tohoto po\u010D\u00EDta\u010De se nem\u016F\u017Eete p\u0159ihl\u00E1sit. -authenticationFailure.InvalidLoginTimeException = P\u0159ihl\u00E1\u0161en\u00ED v tento \u010Das pro V\u00E1\u0161 \u00FA\u010Det povoleno. -authenticationFailure.UNKNOWN = Neplatn\u00E9 p\u0159ihla\u0161ovac\u00ED \u00FAdaje. - -copyright = Copyright © 2005–2012 Jasig, Inc. V\u0161echna pr\u00E1va vyhrazena. - -error.invalid.loginticket = Nen\u00ED mo\u017En\u00E9 znovu odeslat formul\u00E1\u0159, kter\u00FD ji\u017E byl odesl\u00E1n. - -logo.title = j\u00EDt na str\u00E1nky Jasig - -password.expiration.loginsRemaining = Zb\u00FDv\u00E1 V\u00E1m {0} p\u0159ihl\u00E1\u0161en\u00ED, ne\u017E budete MUSET zm\u011Bnit sv\u00E9 heslo. -password.expiration.warning = Va\u0161e heslo vypr\u0161\u00ED za {0} dn\u00ED. Zm\u011B\u0148te pros\u00EDm ihned sv\u00E9 heslo. - -required.password = Heslo je povinn\u00FD \u00FAdaj. -required.username = U\u017Eivatelsk\u00E9 jm\u00E9no je povinn\u00FD \u00FAdaj. - -screen.accountdisabled.heading = Tento \u00FA\u010Det byl zak\u00E1z\u00E1n. -screen.accountdisabled.message = Pro obnoven\u00ED p\u0159\u00EDstupu kontaktujte pros\u00EDm sv\u00E9ho syst\u00E9mov\u00E9ho administr\u00E1tora. -screen.accountlocked.heading = Tento \u00FA\u010Det byl uzam\u010Den. -screen.accountlocked.message = Pro obnoven\u00ED p\u0159\u00EDstupu kontaktujte pros\u00EDm sv\u00E9ho syst\u00E9mov\u00E9ho administr\u00E1tora. -screen.badhours.heading = V\u00E1\u0161 \u00FA\u010Det nem\u00E1 povolen\u00ED k p\u0159ihl\u00E1\u0161en\u00ED v tomto \u010Dase. -screen.badhours.message = Zkuste to pros\u00EDm pozd\u011Bji. -screen.badworkstation.heading = Z tohoto po\u010D\u00EDta\u010De se nem\u016F\u017Eete p\u0159ihl\u00E1sit. -screen.badworkstation.message = Pro obnoven\u00ED p\u0159\u00EDstupu kontaktujte pros\u00EDm sv\u00E9ho syst\u00E9mov\u00E9ho administr\u00E1tora. -screen.blocked.header = P\u0159\u00EDstup odep\u0159en -screen.blocked.message = Zadal(a) jste \u0161patn\u00E9 heslo p\u0159\u00EDli\u0161 \u010Dasto. P\u0159\u00EDtsup byl do\u010Dasn\u011B zablokov\u00E1n. -#Confirmation Screen Messages -screen.confirmation.message = Pro p\u0159echod na web klikn\u011Bte zde. -screen.expiredpass.heading = Va\u0161e heslo ji\u017E n\u011Bn\u00ED platn\u00E9. -screen.expiredpass.message = Zm\u011B\u0148te pros\u00EDm sv\u00E9 heslo. -#Logout Screen Messages -screen.logout.header = \u00DAsp\u011B\u0161n\u00E9 odhl\u00E1\u0161en\u00ED -screen.logout.redirect = Web ze kter\u00E9ho jste sem p\u0159i\u0161li doporu\u010Dil odkaz, kam pokra\u010Dovat. -screen.logout.security = Z bezpe\u010Dnostn\u00EDch d\u016Fvod\u016F uzav\u0159ete v\u0161echna okna prohl\u00ED\u017Ee\u010De. -screen.logout.success = \u00DAsp\u011B\u0161n\u011B jste se odhl\u00E1sili od Centr\u00E1ln\u00ED Autentiza\u010Dn\u00ED Slu\u017Eby. -screen.mustchangepass.heading = Mus\u00EDte zm\u011Bnit sv\u00E9 heslo. -screen.mustchangepass.message = Zm\u011B\u0148te pros\u00EDm sv\u00E9 heslo. -screen.oauth.confirm.allow = Povolit -screen.oauth.confirm.header = Autorizace -screen.oauth.confirm.message = Chcete povolit p\u0159\u00EDstup ke sv\u00E9mu profilu pro "{0}"? -screen.service.empty.error.message = Registr slu\u017Eeb CASu je pr\u00E1zdn\u00FD a nem\u00E1 definovan\u00E9 \u017E\u00E1dn\u00E9 slu\u017Eby. Aplikace, kter\u00E9 chcete autentizovat pomoc\u00ED CASu mus\u00EDte explicitn\u011B uv\u00E9st v registru slu\u017Eeb. -#Service Error Messages -screen.service.error.header = Aplikace nen\u00ED autorizovan\u00E1 k pou\u017Eit\u00ED p\u0159ihl\u0161ov\u00E1n\u00ED pomoc\u00ED CASu. -screen.service.error.message = Aplikace ke kter\u00E9 se sna\u017E\u00EDte p\u0159ihl\u00E1sit nen\u00ED opr\u00E1vn\u011Bna k vyu\u017Eit\u00ED CASu. -screen.service.sso.error.header = Pro tuto slu\u017Ebu je po\u017Eadov\u00E1no op\u011Btovn\u00E9 p\u0159ihl\u00E1\u0161en\u00ED -screen.service.sso.error.message = Pokou\u0161\u00EDte se p\u0159istoupit ke slu\u017Eb\u011B, kter\u00E1 vy\u017Eaduje op\u011Btovn\u00E9 p\u0159ihl\u00E1\u0161en\u00ED. Zkuste se pros\u00EDm p\u0159ihl\u00E1sit znovu. -#Generic Success Screen Messages -screen.success.header = \u00DAsp\u011B\u0161n\u00E9 p\u0159ihl\u00E1\u0161en\u00ED -screen.success.security = Z bezpe\u010Dnostn\u00EDch d\u016Fvod\u016F se po ukon\u010Den\u00ED pr\u00E1ce odhla\u0161te a zav\u0159ete v\u0161echna okna prohl\u00ED\u017Ee\u010De! -screen.success.success = \u00DAsp\u011B\u0161n\u011B jste se p\u0159ihl\u00E1sili k Centr\u00E1ln\u00ED Autentika\u010Dn\u00ED Slu\u017Eb\u011B. -screen.unavailable.heading = CAS nen\u00ED dostupn\u00FD -screen.unavailable.message = P\u0159i zpracov\u00E1n\u00ED Va\u0161eho po\u017Eadavku do\u0161lo k chyb\u011B. Uv\u011Bdomte pros\u00EDm syst\u00E9movou podporu nebo to zkuste znovu. -screen.welcome.button.clear = VY\u010CISTIT -screen.welcome.button.login = P\u0158IHL\u00C1SIT -screen.welcome.instructions = Zadejte sv\u00E9 u\u017Eivatelsk\u00E9 jm\u00E9no a heslo -screen.welcome.label.netid = U\u017Eivatelsk\u00E9 jm\u00E9no -screen.welcome.label.netid.accesskey = u -screen.welcome.label.password = Heslo: -screen.welcome.label.password.accesskey = h -screen.welcome.label.warn = Upozornit p\u0159ed p\u0159ihl\u00E1\u0161en\u00ED k jin\u00E9 aplikaci. -screen.welcome.label.warn.accesskey = z -screen.welcome.security = Z bezpe\u010Dnostn\u00EDch d\u016Fvod\u016F se po ukon\u010Den\u00ED pr\u00E1ce odhla\u0161te a zav\u0159ete v\u0161echna okna prohl\u00ED\u017Ee\u010De! -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -screen.welcome.welcome = Gratulujeme, \u00FAsp\u011B\u0161n\u011B jste zprovoznili CAS! Pro zji\u0161t\u011Bn\u00ED, jak se p\u0159ihl\u00E1sit, prohl\u00E1dn\u011Bte si v\u00FDchoz\u00ED konfiguraci autentifika\u010Dn\u00EDho handleru. diff --git a/cas-server-webapp/src/main/resources/messages_de.properties b/cas-server-webapp/src/main/resources/messages_de.properties deleted file mode 100644 index f9209bd6ca..0000000000 --- a/cas-server-webapp/src/main/resources/messages_de.properties +++ /dev/null @@ -1,109 +0,0 @@ -# Welcome Screen Messages GERMAN - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Glückwunsch, Sie haben Ihr CAS System zum Laufen gebracht! Der voreingestellte Authentication Handler gewährt Einlass, wenn der Benutzername dem Passwort entspricht: Nur zu, probieren Sie es aus. -screen.welcome.security=Aus Sicherheitsgründen sollten Sie bei Verlassen der passwortgeschützten Bereiche sich explizit ausloggen und Ihren Webbrowser schließen! -screen.welcome.redirecting=Umleiten Sie auf die Homepage in einem zweiten... -screen.welcome.instructions=Bitte geben Sie Ihre Jasig NetID und Ihr Passwort ein. -screen.welcome.label.netid=NetID: -screen.welcome.label.netid.accesskey=n -screen.welcome.label.password=Passwort: -screen.welcome.label.password.accesskey=p -screen.welcome.label.warn=Ich möchte gewarnt werden, bevor ich mich in einen anderen Bereich einlogge. -screen.welcome.label.warn.accesskey=w -screen.welcome.button.login=ANMELDEN -screen.welcome.button.clear=LÖSCHEN - -logo.title=zur Jasig Seite wechseln -copyright=Copyright © 2005–2012 Jasig, Inc. Alle Rechte vorbehalten. - -# Blocked Errors Page -screen.blocked.header=Zugriff verweigert -screen.blocked.message=Das Kennwort f\u00FCr den Benutzer wurde zu oft falsch eingegeben. Der Zugriff wird gedrosselt. - -#Confirmation Screen Messages -screen.confirmation.message=Klicken Sie hier um zu der zuvor angeforderten Seite zurückzukehren. - -#Generic Success Screen Messages -screen.success.header=Anmeldung erfolgreich -screen.success.success=Sie haben sich erfolgreich am Central Authentication Service angemeldet. -screen.success.security=Aus Sicherheitsgründen sollten Sie bei Verlassen der passwortgeschützten Bereiche sich explizit ausloggen und Ihren Webbrowser schliessen! - -#Logout Screen Messages -screen.logout.header=Abmeldung erfolgreich -screen.logout.success=Sie haben sich erfolgreich vom Central Authentication Service abgemeldet. -screen.logout.security=Aus Sicherheitsgründen sollten Sie den Browser schliessen. -screen.logout.redirect=Der Service, von dem Sie herkommen, hat einen Link angegeben, den Sie verfolgen k\u00F6nnen, indem Sie hier klicken. - -screen.service.sso.error.header=Eine Neuanmeldung ist erforderlich, um auf den Service zuzugreifen. -screen.service.sso.error.message=Der Service, für den Sie versucht haben, sich zu authentifizieren, hat nicht das Recht, CAS zu benutzen. - -error.invalid.loginticket=Sie können kein Formular erneut abschicken, das bereits übertragen wurde. -required.username=Benutzername ist ein Pflichtfeld. -required.password=Passwort ist ein Pflichtfeld. - -# Authentication failure messages -authenticationFailure.AccountDisabledException=Dieses Konto wurde deaktiviert. -authenticationFailure.AccountLockedException=Dieses Konto wurde gesperrt. -authenticationFailure.CredentialExpiredException=Ihr Kennwort ist abgelaufen. -authenticationFailure.InvalidLoginLocationException=Sie k\u00F6nnen sich von dieser Workstation nicht anmelden. -authenticationFailure.InvalidLoginTimeException=Ihrem Konto ist es nicht gestattet sich zu diesem Zeitpunkt anzumelden. -authenticationFailure.AccountNotFoundException=Ung\u00FCltige Anmeldedaten. -authenticationFailure.FailedLoginException=Ung\u00FCltige Anmeldedaten. -authenticationFailure.UNKNOWN=Ung\u00FCltige Anmeldedaten. - -INVALID_REQUEST_PROXY='pgt' und 'targetService' Parameter werden beide benötigt -INVALID_TICKET_SPEC=Das Ticket entspricht nicht den Überprüfungsregeln. Ein möglicher Fehler könnte sein, dass versucht wurde, ein Proxy Ticket mit einem Service Ticket Validierer zu überprüfen, oder man sich nicht an den renew true Request gehalten hat. -INVALID_REQUEST='service' und 'ticket' Parameter werden beide benötigt -INVALID_TICKET=Ticket ''{0}'' wurde nicht anerkannt -INVALID_SERVICE=Ticket ''{0}'' passt nicht zum angegebenen Service. Der ursprüngliche Service war ''{1}'' und der übermittelte Service war ''{2}''. -INVALID_PROXY_CALLBACK=Die angegebene Proxy-Callback-Url ''{0}'' kann nicht authentifiziert werden. -UNAUTHORIZED_SERVICE_PROXY=Dem angegebenen Service ''{0}'' ist es nicht gestattet eine CAS Proxy Authentifizierung zu verwenden. - -screen.service.error.header=Applikation nicht berechtigt CAS zu verwenden -screen.service.error.message=Die Applikation, mit der eine Authentifkation versucht wurde, ist nicht berechtigt CAS zu verwenden. -screen.service.empty.error.message=Die Service-Registrierung des CAS Server ist leer und hat keine Service-Definitionen.\ -Applikationen, welche \u00FCber CAS authentifizieren m\u00F6chten, m\u00FCssen in der Services-Registrierung definiert werden. - -# Password policy -password.expiration.warning=Ihr Kennwort l\u00E4uft in {0} Tagen ab. Bitte \u00E4ndern Sie Ihr Kennwort. -password.expiration.loginsRemaining=Sie haben {0} anmeldungen \u00FCbrig, bevor Sie Ihr Kennwort \u00E4ndern m\u00FCssen. -screen.accountdisabled.heading=Dieses Konto wurde deaktiviert. -screen.accountdisabled.message=Bitte kontaktieren Sie Ihren System Administrator um wieder Zugriff zu erhalten. -screen.accountlocked.heading=Dieses Konto wurde gesperrt. -screen.accountlocked.message=Bitte kontaktieren Sie den Systemadministrator um wieder Zugang zu erlangen. -screen.expiredpass.heading=Ihr Kennwort ist abgelaufen. -screen.expiredpass.message=Bitte ändern Sie Ihr Kennwort. -screen.mustchangepass.heading=Sie müssen Ihr Kennwort ändern. -screen.mustchangepass.message=Bitte ändern Sie Ihr Kennwort. -screen.badhours.heading=Ihrem Konto ist es nicht gestattet sich zu diesem Zeitpunkt anzumelden. -screen.badhours.message=Bitte versuchen Sie es später noch einmal. -screen.badworkstation.heading=Sie können sich von dieser Workstation aus nicht anmelden. -screen.badworkstation.message=Bitte kontaktieren Sie Ihren System Administrator um Zugriff zu erhalten. - -# OAuth -screen.oauth.confirm.header=Authorisierung -screen.oauth.confirm.message=Wollen Sie "{0}" vollen Zugriff auf Ihr Profil gestatten? -screen.oauth.confirm.allow=Erlauben - -# Unavailable -screen.unavailable.heading=CAS ist nicht verf\u00FCgbar -screen.unavailable.message=Beim Verarbeiten Ihrer Anfrage ist ein Fehler aufgetreten. Bitte informieren Sie Ihren Support oder versuchen Sie es noch einmal. diff --git a/cas-server-webapp/src/main/resources/messages_es.properties b/cas-server-webapp/src/main/resources/messages_es.properties deleted file mode 100644 index b3700abd30..0000000000 --- a/cas-server-webapp/src/main/resources/messages_es.properties +++ /dev/null @@ -1,114 +0,0 @@ -#Author: Joaquin Recio, Jose Luis Huertas, Juan Paulo Soto, and Alex Henrie - -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=\u00a1Felicidades por iniciar CAS correctamente! Para aprender c\u00f3mo autenticar, por favor repase la configuraci\u00f3n del manejador de configuraci\u00f3n por defecto. -screen.welcome.security=Por razones de seguridad, por favor cierre su sesi\u00f3n y su navegador web cuando haya terminado de acceder a los servicios que requieren autenticaci\u00f3n. -screen.welcome.redirecting=Que volver a dirigir a la pagina de inicio en un segundo... -screen.welcome.instructions=Introduzca su nombre de usuario y contrase\u00f1a. -screen.welcome.label.netid=Nombre de usuario: -screen.welcome.label.netid.accesskey=u -screen.welcome.label.password=Contrase\u00f1a: -screen.welcome.label.password.accesskey=c -screen.welcome.label.warn=Avisarme antes de abrir sesi\u00f3n en otros sitios. -screen.welcome.label.warn.accesskey=a -screen.welcome.button.login=INICIAR SESI\u00d3N -screen.welcome.button.clear=LIMPIAR -screen.welcome.or=O -screen.welcome.link.forgot=clave perdida -screen.welcome.link.newaccount=Crear una cuenta - -logo.title=ir a la p\u00e1gina principal de Jasig -copyright=Copyright © 2005–2012 Jasig, Inc. Todos los derechos reservados. - -# Blocked Errors Page -screen.blocked.header=Acceso Denegado -screen.blocked.message=Ha introducido una contrase\u00f1a equivocada por el usuario demasiadas veces. Se le ha restringido. - -#Confirmation Screen Messages -screen.confirmation.message=Haga clic aqu\u00ed para ir a la aplicaci\u00f3n. - -#Generic Success Screen Messages -screen.success.header=Inicio de sesi\u00f3n exitoso. -screen.success.success=Ha iniciado con \u00e9xito su sesi\u00f3n en el Servicio de Autenticaci\u00f3n Central. -screen.success.security=Por razones de seguridad, por favor cierre su sesi\u00f3n y su navegador web cuando haya terminado de acceder a los servicios que requieren autenticaci\u00f3n. - -#Logout Screen Messages -screen.logout.header=Cierre de sesi\u00f3n exitoso. -screen.logout.success=Ha cerrado con \u00e9xito su sesi\u00f3n del Servicio de Autenticaci\u00f3n Central. -screen.logout.security=Por razones de seguridad, cierre su navegador web. -screen.logout.redirect=El servicio desde el cual ha llegado ha proporcionado un enlace que puede seguir por hacer clic aqu\u00ed. - -screen.service.sso.error.header=Reautenticaci\u00f3n requerida para acceder a este servicio. -screen.service.sso.error.message=Intent\u00f3 acceder a un servicio que requiere autenticaci\u00f3n sin reautenticar. Por favor intente autenticar de nuevo. - -error.invalid.loginticket=No puede intentar reenviar un formulario que ya se ha enviado. -required.username=El nombre de usuario es un campo requerido. -required.password=La contrase\u00f1a es un campo requerido. - -# Authentication failure messages -authenticationFailure.AccountDisabledException=Se ha deshabilitado esta cuenta. -authenticationFailure.AccountLockedException=Se ha bloqueado esta cuenta. -authenticationFailure.CredentialExpiredException=Su contrase\u00f1a ha caducado. -authenticationFailure.InvalidLoginLocationException=No puede iniciar sesi\u00f3n desde esta estaci\u00f3n de trabajo. -authenticationFailure.InvalidLoginTimeException=Est\u00e1 prohibido iniciar sesi\u00f3n con su cuenta en este momento. -authenticationFailure.AccountNotFoundException=Credenciales inv\u00e1lidos. -authenticationFailure.FailedLoginException=Credenciales inv\u00e1lidos. -authenticationFailure.UNKNOWN=Credenciales inv\u00e1lidos. - -INVALID_REQUEST_PROXY=ambos de los par\u00e1metros 'pgt' y 'targetService' se requieren -INVALID_TICKET_SPEC=El ticket fall\u00f3 la especificaci\u00f3n de validaci\u00f3n. Los errores posibles pueden incluir intentar validar un ticket de proxy mediante un validador de tickets de servei, o no cumplir con la petici\u00f3n de renovaci\u00f3n (renew true). -INVALID_REQUEST=ambos de los par\u00e1metros 'service' y 'ticket' se requieren -INVALID_TICKET=No se ha reconocido el ticket ''{0}'' -INVALID_SERVICE=Ticket ''{0}'' no coincide con el servicio proporcionado. El servicio original era ''{1}'' y el servicio proporcionado era ''{2}''. -INVALID_PROXY_CALLBACK=La direcci\u00f3n web de retrollamada de proxy ''{0}'' no se pudo autenticar. -UNAUTHORIZED_SERVICE_PROXY=El servicio proporcionado ''{0}'' no est\u00e1 autorizado a usar la autenticaci\u00f3n de proxy CAS. - -screen.service.error.header=Aplicaci\u00f3n no autorizada a usar CAS -screen.service.error.message=La aplicaci\u00f3n que usted ha intentado autenticar no est\u00e1 autorizada a usar CAS. -screen.service.empty.error.message=El registro de servicios del CAS est\u00e1 vac\u00edo y no tiene definiciones de servicio. \ -Las aplicaciones que quieren autenticar con CAS deben ser explic\u00edtamente definidas en el registro de servcios. - -# Password policy -password.expiration.warning=Su contrase\u00f1a caduca en {0} d\u00edas. Por favor cambie su contrase\u00f1a ahora. -password.expiration.loginsRemaining=Tiene {0} inicios de sesi\u00f3n restantes antes que DEBE cambiar su contrase\u00f1a. -screen.accountdisabled.heading=Se ha deshabilitado esta cuenta. -screen.accountdisabled.message=Por favor contacte al administrador de sistema para recobrar el acceso. -screen.accountlocked.heading=Se ha bloqueado esta cuenta. -screen.accountlocked.message=Por favor contacte al administrador de sistema para recobrar el acceso. -screen.expiredpass.heading=Su contrase\u00f1a ha caducado. -screen.expiredpass.message=Por favor cambie su contrase\u00f1a. -screen.mustchangepass.heading=Debe cambiar su contrase\u00f1a. -screen.mustchangepass.message=Por favor cambie su contrase\u00f1a. -screen.badhours.heading=Est\u00e1 prohibido iniciar sesi\u00f3n con su cuenta en este momento. -screen.badhours.message=Por favor intente m\u00e1s tarde. -screen.badworkstation.heading=No puede iniciar sesi\u00f3n desde esta estaci\u00f3n de trabajo. -screen.badworkstation.message=Por favor contacte al administrador de sistema para recobrar el acceso. - -# OAuth -screen.oauth.confirm.header=Autorizaci\u00f3n -screen.oauth.confirm.message=\u00bfQuiere conceder acceso a su perfil completo a "{0}"? -screen.oauth.confirm.allow=Permitir - -# Unavailable -screen.unavailable.heading=CAS no est\u00e1 disponible -screen.unavailable.message=Hubo un error al intentar cumplir con su petici\u00f3n. Por favor notifique a su servicio de asistencia o intente otra vez. diff --git a/cas-server-webapp/src/main/resources/messages_fa.properties b/cas-server-webapp/src/main/resources/messages_fa.properties deleted file mode 100644 index 295977d735..0000000000 --- a/cas-server-webapp/src/main/resources/messages_fa.properties +++ /dev/null @@ -1,71 +0,0 @@ -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=\u0645\u0648\u0641\u0642 \u0634\u062F\u06CC\u062F CAS \u0631\u0627 \u0628\u0647 \u0635\u0648\u0631\u062A \u0622\u0646\u0644\u0627\u06CC\u0646 \u0628\u0627\u0631\u06AF\u0630\u0627\u0631\u06CC \u06A9\u0646\u06CC\u062F! \u0645\u062F\u06CC\u0631 \u062A\u0627\u06CC\u06CC\u062F \u067E\u06CC\u0634 \u0641\u0631\u0636\u060C \u062A\u0627\u06CC\u06CC\u062F \u0645\u06CC\u06A9\u0646\u062F \u0686\u0647 \u0632\u0645\u0627\u0646\u06CC \u0646\u0627\u0645 \u06A9\u0627\u0631\u0628\u0631\u06CC \u0648 \u0631\u0645\u0632 \u0648\u0631\u0648\u062F \u0647\u0645\u062E\u0648\u0627\u0646\u06CC \u062F\u0627\u0631\u0646\u062F: \u0645\u0631\u0627\u062D\u0644 \u0631\u0627 \u0627\u062F\u0627\u0645\u0647 \u062F\u0647\u06CC\u062F \u0648 \u0627\u0645\u062A\u062D\u0627\u0646 \u06A9\u0646\u06CC\u062F. -screen.welcome.security=\u0628\u0647 \u062F\u0644\u0627\u06CC\u0644 \u0627\u0645\u0646\u06CC\u062A\u06CC \u0632\u0645\u0627\u0646\u06CC \u06A9\u0647 \u062F\u06CC\u06AF\u0631 \u0646\u06CC\u0627\u0632\u06CC \u0628\u0647 \u062F\u0633\u062A\u06CC\u0627\u0628\u06CC \u0628\u0647 \u0633\u0631\u0648\u06CC\u0633\u0647\u0627\u06CC\u06CC \u06A9\u0647 \u0627\u062D\u062A\u06CC\u0627\u062C \u0628\u0647 \u062A\u0627\u06CC\u06CC\u062F \u062F\u0627\u0631\u0646\u062F \u0646\u062F\u0627\u0634\u062A\u06CC\u062F\u060C \u0627\u0632 \u067E\u0627\u06CC\u06AF\u0627\u0647 \u062E\u0627\u0631\u062C \u0634\u062F\u0647 \u0645\u0631\u0648\u0631\u06AF\u0631 \u062E\u0648\u062F \u0631\u0627 \u0628\u0628\u0646\u062F\u06CC\u062F! -screen.welcome.instructions=\u0646\u0627\u0645 \u06A9\u0627\u0631\u0628\u0631\u06CC \u0648 \u0631\u0645\u0632 \u0648\u0631\u0648\u062F \u0631\u0627 \u0648\u0627\u0631\u062F \u06A9\u0646\u06CC\u062F -screen.welcome.label.netid=\u0646\u0627\u0645 \u06A9\u0627\u0631\u0628\u0631\u06CC -screen.welcome.label.netid.accesskey= -screen.welcome.label.password=\u0631\u0645\u0632 \u0648\u0631\u0648\u062F -screen.welcome.label.password.accesskey= -screen.welcome.label.warn=\u0642\u0628\u0644 \u0627\u0632 \u0648\u0631\u0648\u062F \u0628\u0647 \u0633\u0627\u06CC\u062A\u0647\u0627\u06CC \u062F\u06CC\u06AF\u0631 \u0628\u0647 \u0645\u0646 \u0647\u0634\u062F\u0627\u0631 \u0628\u062F\u0647 -screen.welcome.label.warn.accesskey= -screen.welcome.button.login=\u0648\u0631\u0648\u062F -screen.welcome.button.clear=\u0627\u0646\u0635\u0631\u0627\u0641 - -logo.title=\u0628\u0647 \u0635\u0641\u062D\u0647 \u0627\u0635\u0644\u06CC Jasig \u0628\u0631\u0648 -copyright=\u062D\u0642 \u0646\u0634\u0631 © 2005 - 2012 Jasig, Inc. \u06A9\u0644\u06CC\u0647 \u062D\u0642\u0648\u0642 \u0645\u062D\u0641\u0648\u0638 \u0627\u0633\u062A - -# Blocked Errors Page -screen.blocked.header=\u062F\u0633\u062A\u0631\u0633\u06CC \u0645\u0645\u06A9\u0646 \u0646\u06CC\u0633\u062A -screen.blocked.message=\u0628\u0647 \u062F\u0641\u0639\u0627\u062A \u0628\u0631\u0627\u06CC \u0646\u0627\u0645 \u06A9\u0627\u0631\u0628\u0631\u06CC\u060C \u0631\u0645\u0632 \u0648\u0631\u0648\u062F \u0631\u0627 \u0627\u0634\u062A\u0628\u0627\u0647 \u0648\u0627\u0631\u062F \u06A9\u0631\u062F\u0647\u0627\u06CC\u062F. \u0627\u0632 \u0648\u0631\u0648\u062F \u0634\u0645\u0627 \u062C\u0644\u0648\u06AF\u06CC\u0631\u06CC \u0634\u062F\u0647 \u0627\u0633\u062A. - -#Confirmation Screen Messages -screen.confirmation.message=\u0628\u0631\u0627\u06CC \u0648\u0631\u0648\u062F \u0628\u0647 \u0628\u0631\u0646\u0627\u0645\u0647 \u0627\u06CC\u0646\u062C\u0627 \u0631\u0627 \u06A9\u0644\u06CC\u06A9 \u06A9\u0646\u06CC\u062F - -#Generic Success Screen Messages -screen.success.header=\u0648\u0631\u0648\u062F \u0645\u0648\u0641\u0642\u06CC\u062A \u0622\u0645\u06CC\u0632 \u0628\u0648\u062F -screen.success.success=\u0628\u0627 \u0645\u0648\u0641\u0642\u06CC\u062A \u0648\u0627\u0631\u062F \u067E\u0627\u06CC\u06AF\u0627\u0647 \u062A\u0627\u06CC\u06CC\u062F \u0645\u0631\u06A9\u0632\u06CC CAS \u0634\u062F\u06CC\u062F -screen.success.security=\u0628\u0647 \u062F\u0644\u0627\u06CC\u0644 \u0627\u0645\u0646\u06CC\u062A\u06CC \u0632\u0645\u0627\u0646\u06CC \u06A9\u0647 \u062F\u06CC\u06AF\u0631 \u0646\u06CC\u0627\u0632\u06CC \u0628\u0647 \u062F\u0633\u062A\u06CC\u0627\u0628\u06CC \u0628\u0647 \u0633\u0631\u0648\u06CC\u0633 \u0647\u0627\u06CC\u06CC \u06A9\u0647 \u0627\u062D\u062A\u06CC\u0627\u062C \u0628\u0647 \u062A\u0627\u06CC\u06CC\u062F \u062F\u0627\u0631\u0646\u062F \u0646\u062F\u0627\u0634\u062A\u06CC\u062F\u060C \u0627\u0632 \u067E\u0627\u06CC\u06AF\u0627\u0647 \u062E\u0627\u0631\u062C \u0634\u062F\u0647 \u0645\u0631\u0648\u0631\u06AF\u0631 \u062E\u0648\u062F \u0631\u0627 \u0628\u0628\u0646\u062F\u06CC\u062F! - -#Logout Screen Messages -screen.logout.header=\u062E\u0631\u0648\u062C \u0627\u0632 \u067E\u0627\u06CC\u06AF\u0627\u0647 \u0645\u0648\u0641\u0642\u06CC\u062A \u0622\u0645\u06CC\u0632 \u0628\u0648\u062F -screen.logout.success=\u0628\u0627 \u0645\u0648\u0641\u0642\u06CC\u062A \u0627\u0632 \u067E\u0627\u06CC\u06AF\u0627\u0647 \u062A\u0627\u06CC\u06CC\u062F \u0645\u0631\u06A9\u0632\u06CC CAS \u062E\u0627\u0631\u062C \u0634\u062F\u06CC\u062F -screen.logout.security=\u0628\u0631\u0627\u06CC \u062D\u0641\u0638 \u0627\u0645\u0646\u06CC\u062A \u0627\u0632 \u0645\u0631\u0648\u0631\u06AF\u0631 \u062E\u0648\u062F \u062E\u0627\u0631\u062C \u0634\u0648\u06CC\u062F -screen.logout.redirect=\u0633\u0631\u0648\u06CC\u0633\u06CC \u0627\u0631\u062C\u0627\u0639 \u062F\u0647\u0646\u062F\u0647 \u0634\u0645\u0627 \u0627\u06CC\u0646 \u0644\u06CC\u0646\u06A9 \u0631\u0627 \u062A\u0648\u0644\u06CC\u062F \u06A9\u0631\u062F\u0647 \u0627\u0633\u062A. \u0628\u0631\u0627\u06CC \u0627\u062F\u0627\u0645\u0647 \u0644\u06CC\u0646\u06A9 \u0631\u0627 \u06A9\u0644\u06CC\u06A9 \u06A9\u0646\u06CC\u062F. - -screen.service.sso.error.header=\u0628\u0631\u0627\u06CC \u062F\u0633\u062A\u0631\u0633\u06CC \u0628\u0647 \u0627\u06CC\u0646 \u0633\u0631\u0648\u06CC\u0633 \u0646\u06CC\u0627\u0632 \u0628\u0647 \u062A\u0627\u06CC\u06CC\u062F \u062F\u0648\u0628\u0627\u0631\u0647 \u062F\u0627\u0631\u06CC\u062F -screen.service.sso.error.message=\u0633\u0639\u06CC \u062F\u0627\u0634\u062A\u06CC\u062F \u0628\u062F\u0648\u0646 \u062A\u0627\u06CC\u06CC\u062F \u062F\u0648\u0628\u0627\u0631\u0647\u060C \u0628\u0647 \u0633\u0631\u0648\u06CC\u0633\u06CC \u062F\u0633\u062A\u0631\u0633\u06CC \u067E\u06CC\u062F\u0627 \u06A9\u0646\u06CC\u062F \u06A9\u0647 \u0646\u06CC\u0627\u0632 \u0628\u0647 \u062A\u0627\u06CC\u06CC\u062F \u062F\u0627\u0631\u062F. \u0644\u0637\u0641\u0627\u064B \u0628\u0639\u062F \u0627\u0632 \u062A\u0627\u06CC\u06CC\u062F \u062F\u0648\u0628\u0627\u0631\u0647 \u0627\u0645\u062A\u062D\u0627\u0646 \u06A9\u0646\u06CC\u062F - -error.invalid.loginticket=\u0646\u0645\u06CC\u062A\u0648\u0627\u0646\u06CC\u062F \u0641\u0631\u0645\u06CC \u06A9\u0647 \u0627\u0631\u0633\u0627\u0644 \u0634\u062F\u0647 \u0631\u0627 \u062F\u0648\u0628\u0627\u0631\u0647 \u0627\u0631\u0633\u0627\u0644 \u06A9\u0646\u06CC\u062F -required.username=\u0648\u0627\u0631\u062F \u06A9\u0631\u062F\u0646 \u0646\u0627\u0645 \u06A9\u0627\u0631\u0628\u0631\u06CC \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A -required.password=\u0648\u0627\u0631\u062F \u06A9\u0631\u062F\u0646 \u0631\u0645\u0632 \u0648\u0631\u0648\u062F \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A -error.authentication.credentials.bad=\u0646\u0627\u0645 \u06A9\u0627\u0631\u0628\u0631\u06CC \u0648 \u0631\u0645\u0632 \u0648\u0631\u0648\u062F \u0635\u062D\u06CC\u062D \u0646\u0645\u06CC\u0628\u0627\u0634\u062F -error.authentication.credentials.unsupported=>CAS \u0627\u06CC\u0646 \u0646\u0627\u0645 \u06A9\u0627\u0631\u0628\u0631\u06CC \u0648 \u0631\u0645\u0632 \u0648\u0631\u0648\u062F \u0631\u0627 \u067E\u0634\u062A\u06CC\u0627\u0646\u06CC \u0646\u0645\u06CC\u06A9\u0646\u062F - -INVALID_REQUEST_PROXY=\u067E\u0627\u0631\u0627\u0645\u062A\u0631\u0647\u0627\u06CC pgt \u0648 targetService \u0647\u0631 \u062F\u0648 \u0627\u0644\u0632\u0627\u0645\u06CC \u0647\u0633\u062A\u0646\u062F -INVALID_TICKET_SPEC=\u0634\u0646\u0627\u0633\u0647 \u0645\u0648\u0631\u062F \u062A\u0627\u06CC\u06CC\u062F \u0642\u0631\u0627\u0631 \u0646\u06AF\u0631\u0641\u062A. \u062E\u0637\u0627\u0647\u0627\u06CC \u0645\u0645\u06A9\u0646 \u0645\u06CC\u062A\u0648\u0627\u0646\u062F \u0634\u0627\u0645\u0644 \u0633\u0639\u06CC \u062F\u0631 \u0645\u0648\u0631\u062F \u062A\u0627\u06CC\u06CC\u062F \u0642\u0631\u0627\u0631 \u062F\u0627\u062F\u0646 \u0634\u0646\u0627\u0633\u0647-\u06CC \u067E\u0631\u0627\u06A9\u0633\u06CC \u0627\u0632 \u0637\u0631\u06CC\u0642 \u0633\u06CC\u0633\u062A\u0645 \u062A\u0627\u06CC\u06CC\u062F \u06A9\u0646\u0646\u062F\u0647\u06CC \u0634\u0646\u0627\u0633\u0647\u06CC \u0633\u0631\u0648\u06CC\u0633 \u06CC\u0627 \u0647\u0645\u062E\u0648\u0627\u0646\u06CC \u0646\u062F\u0627\u0634\u062A\u0646 \u0628\u0627 \u062F\u0631\u062E\u0648\u0627\u0633\u062A \u062A\u062C\u062F\u06CC\u062F \u0634\u062F\u0647 \u0628\u0627\u0634\u062F. -INVALID_REQUEST=\u067E\u0627\u0631\u0627\u0645\u062A\u0631\u0647\u0627\u06CC service\u0648 ticket \u0647\u0631 \u062F\u0648 \u0627\u0644\u0632\u0627\u0645\u06CC \u0647\u0633\u062A\u0646\u062F -INVALID_TICKET=\u0634\u0646\u0627\u0633\u0647 {0} \u0634\u0646\u0627\u0633\u0627\u06CC\u06CC \u0646\u0634\u062F -INVALID_SERVICE=\u0634\u0646\u0627\u0633\u0647 {0} \u0628\u0627 \u0633\u0631\u0648\u06CC\u0633 \u0639\u0631\u0636\u0647 \u0634\u062F\u0647 \u0647\u0645\u062E\u0648\u0627\u0646\u06CC \u0646\u062F\u0627\u0631\u062F. \u0633\u0631\u0648\u06CC\u0633 \u0627\u0635\u0644\u06CC{1} \u0648 \u0633\u0631\u0648\u06CC\u0633 \u0639\u0631\u0636\u0647 \u0634\u062F\u0647{2} \u0628\u0648\u062F\u0647 \u0627\u0633\u062A. - -screen.service.error.header=\u0628\u0631\u0646\u0627\u0645\u0647 \u0628\u0631\u0627\u06CC \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0627\u0632 CAS \u062A\u0627\u06CC\u06CC\u062F \u0646\u0634\u062F\u0647 \u0627\u0633\u062A -screen.service.error.message=\u0628\u0631\u0646\u0627\u0645\u0647\u0627\u06CC \u06A9\u0647 \u0633\u0639\u06CC \u062F\u0631 \u062A\u0627\u06CC\u06CC\u062F \u0622\u0646 \u062F\u0627\u0634\u062A\u06CC\u062F\u060C \u0628\u0631\u0627\u06CC \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0627\u0632 CAS \u0645\u0639\u062A\u0628\u0631 \u0646\u06CC\u0633\u062A. diff --git a/cas-server-webapp/src/main/resources/messages_fr.properties b/cas-server-webapp/src/main/resources/messages_fr.properties deleted file mode 100644 index f3f7142c20..0000000000 --- a/cas-server-webapp/src/main/resources/messages_fr.properties +++ /dev/null @@ -1,116 +0,0 @@ -#Author: Pascal Aubry -#Version: $Revision$ $Date$ -#Since: 3.0.4 - -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Félicitations, votre serveur est en ligne ! Pour savoir comment vous authentifier, merci de regarder la méthode d'authentification définie par défaut. -screen.welcome.security=Pour des raisons de sécurité, veuillez vous déconnecter et fermer votre navigateur lorsque vous avez fini d'accéder aux services authentifiés. -screen.welcome.redirecting=Nous vous redirigeons vers la page d'accueil dans une seconde... -screen.welcome.instructions=Entrez votre identifiant et votre mot de passe. -screen.welcome.label.netid=Identifiant: -screen.welcome.label.netid.accesskey=i -screen.welcome.label.password=Mot de passe: -screen.welcome.label.password.accesskey=m -screen.welcome.label.warn=Prévenez-moi avant d'accéder à d'autres services. -screen.welcome.label.warn.accesskey=p -screen.welcome.button.login=SE CONNECTER -screen.welcome.button.clear=EFFACER -screen.welcome.or=OU -screen.welcome.link.forgot=mot de passe oublié -screen.welcome.link.newaccount=Créer un compte - -logo.title=allez à la page d'accueil Jasig -copyright=Copyright © 2005–2012 Jasig, Inc. Tous droits réservés. - -# Blocked Errors Page -screen.blocked.header=Accès non autorisé -screen.blocked.message=Vous avez saisi un mauvais mot de passe trop de fois de suite. Vous avez été rejeté. - -#Confirmation Screen Messages -screen.confirmation.message=Cliquez ici pour accéder au service. - -#Generic Success Screen Messages -screen.success.header=Connexion réussie -screen.success.success=Vous vous êtes authentifié(e) auprès du Service Central d'Authentification. -screen.success.security=Pour des raisons de sécurité, veuillez vous déconnecter et fermer votre navigateur lorsque vous avez fini d'accéder aux services authentifiés. - -#Logout Screen Messages -screen.logout.header=Déconnexion réussie -screen.logout.success=Vous vous êtes déconnecté(e) du Service Central d'Authentification. -screen.logout.security=Pour des raisons de sécurité, veuillez fermer votre navigateur. -screen.logout.redirect=Le service duquel vous arrivez a fourni un lien que vous pouvez suivre en cliquant ici. - -screen.service.sso.error.header=Une nouvelle authentification est requise pour accéder à ce service. -screen.service.sso.error.message=Vous avez tenté d'accéder à un service qui requiert une nouvelle authentification sans vous authentifier à nouveau. Veuillez vous authentifier de nouveau. - -error.invalid.loginticket=Vous ne pouvez pas re-soumettre un formulaire d'autentification qui a déjà été soumis. -required.username=Vous devez entrer votre identifiant. -required.password=Vous devez entrer votre mot de passe. - -# Authentication failure messages -authenticationFailure.AccountDisabledException=Votre compte a été désactivé. -authenticationFailure.AccountLockedException=Votre compte est bloqué. -authenticationFailure.CredentialExpiredException=Votre mot de passe a expiré. -authenticationFailure.InvalidLoginLocationException=Vous ne pouvez pas vous authentifier depuis cet ordinateur. -authenticationFailure.InvalidLoginTimeException=Vous ne pouvez pas vous authentifier pendant cette période. -authenticationFailure.AccountNotFoundException=Mauvais identifiant / mot de passe. -authenticationFailure.FailedLoginException=Mauvais identifiant / mot de passe. -authenticationFailure.UNKNOWN=Mauvais identifiant / mot de passe. - -INVALID_REQUEST_PROXY=Les paramètres 'pgt' et 'targetService' sont tous deux nécessaires -INVALID_TICKET_SPEC=La validation du ticket est impossible. Les raisons possibles peuvent être la validation d'un Proxy Ticket sur une URL de validation de Service Ticket, ou une mauvaise requête de type renew. -INVALID_REQUEST=Les paramètres 'service' et 'ticket' sont tous deux nécessaires -INVALID_TICKET=Le ticket ''{0}'' est inconnu -INVALID_SERVICE=Le ticket ''{0}'' ne correspond pas au service demandé. Le service original était ''{1}'' et le service demandé était ''{2}''. -INVALID_PROXY_CALLBACK=L''url de rappel du proxy ''{0}'' n''a pu être authentifiée. -UNAUTHORIZED_SERVICE_PROXY=Le service utilisé ''{0}'' n''est pas autorisé à utiliser l''authentification proxy CAS. - -screen.service.error.header=Application non autorisée à utiliser CAS -screen.service.error.message=L'application pour laquelle vous avez tenté de vous authentifier n'est pas autorisée à utiliser CAS. -screen.service.empty.error.message=Aucun service CAS n'a été défini. \ -Les applications qui veulent s'authentifier sur CAS doivent explicitement s'enregistrer dans la base des services. - -# Password policy -password.expiration.warning=Votre mot de passe expire dans {0} jour(s). Merci de changer votre mot de passe maintenant. -password.expiration.loginsRemaining=Il vous reste {0} authentification(s) avant de DEVOIR changer votre mot de passe. -screen.accountdisabled.heading=Ce compte a été désactivé. -screen.accountdisabled.message=Merci de contacter votre administrateur système pour récupérer votre accès. -screen.accountlocked.heading=Votre compte a été bloqué. -screen.accountlocked.message=Merci de contacter votre administrateur pour le débloquer. -screen.expiredpass.heading=Votre mot de passe a expiré. -screen.expiredpass.message=Merci de changer votre mot de passe. -screen.mustchangepass.heading=Vous devez changer votre mot de passe. -screen.mustchangepass.message=Merci de changer votre mot de passe. -screen.badhours.heading=Vous ne pouvez pas vous authentifier durant cette plage horaire. -screen.badhours.message=Merci de réessayer plus tard. -screen.badworkstation.heading=Vous ne pouvez pas vous authentifier depuis cet ordinateur. -screen.badworkstation.message=Merci de contacter votre administrateur système pour récupérer votre accès. - -# OAuth -screen.oauth.confirm.header=Autorisation -screen.oauth.confirm.message=Voulez-vous donner l'accès de votre profil complet à "{0}" ? -screen.oauth.confirm.allow=Autoriser - -# Unavailable -screen.unavailable.heading=CAS est indisponible -screen.unavailable.message=Une erreur s'est produite lors du traitement de votre requête. Merci de contacter votre support ou de réessayer. diff --git a/cas-server-webapp/src/main/resources/messages_hr.properties b/cas-server-webapp/src/main/resources/messages_hr.properties deleted file mode 100644 index fc656debeb..0000000000 --- a/cas-server-webapp/src/main/resources/messages_hr.properties +++ /dev/null @@ -1,70 +0,0 @@ -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -# @author Nebojsa Topolscak -# @author Jasmina Plavac -# University Computing Center - Zagreb, Croatia -# @since 3.1.1 - -#Welcome Screen Messages - -screen.welcome.welcome=\u010cestitamo na uspje\u0161noj instalaciji CAS-a! Inicijalni autentikacijski mehanizam obavlja uspje\u0161nu autentikaciju uno\u0161enjem korisni\u010dkog imena i zaporke iste vrijednosti. Isprobajte! -screen.welcome.security=Iz sigurnosnih razloga molimo vas da se odjavite i zatvorite web preglednik nakon \u0161to zavr\u0161ite s radom u aplikacijama koje zahtijevaju autentikaciju. -screen.welcome.instructions=Unesite korisni\u010dko ime i zaporku. -screen.welcome.label.netid=Korisni\u010dko ime: -screen.welcome.label.netid.accesskey=k -screen.welcome.label.password=Zaporka: -screen.welcome.label.password.accesskey=z -screen.welcome.label.warn=Upozori me prije prijave u druge aplikacije. -screen.welcome.label.warn.accesskey=u -screen.welcome.button.login=PRIJAVA -screen.welcome.button.clear=PONI\u0160TI - -#Confirmation Screen Messages -screen.confirmation.message=Pritisnite ovdje za ulaz u aplikaciju. - -#Generic Success Screen Messages -screen.success.header=Uspje\u0161na prijava -screen.success.success=Uspje\u0161no ste se prijavili u Centralni autentikacijski servis. -screen.success.security=Iz sigurnosnih razloga molimo vas da se odjavite i zatvorite web preglednik nakon \u0161to zavr\u0161ite s radom u aplikacijama koje zahtijevaju autentikaciju. - -#Logout Screen Messages -screen.logout.header=Uspje\u0161na odjava -screen.logout.success=Uspje\u0161no ste se odjavili iz Centralnog autentikacijskog servisa -screen.logout.security=Iz sigurnosnih razloga zatvorite web preglednik. -screen.logout.redirect=Servis pomo\u0107u kojeg ste do\u0161li na straice CAS-a prenio je link koji mo\u017eete slijediti pritiskom ovdje. - -screen.service.sso.error.header=Za pristup ovom servisu potrebna je ponovna autentikacija. -screen.service.sso.error.message=Poku\u0161ali ste pristupiti servisu koji zahtijeva autentikaciju, pri \u010demu se niste ponovno autenticirali. Molimo vas poku\u0161ajte se ponovno autenticirati pritiskom ovdje. - -error.invalid.loginticket=Sadr\u017eaj forme ve\u0107 je poslan. Ponovno slanje nije dozvoljeno. -required.username=Korisni\u010dko ime je obavezno polje. -required.password=Zaporka je obavezno polje. -error.authentication.credentials.bad=Korisni\u010dko ime i(li) zaporka nisu ispravni. -error.authentication.credentials.unsupported=CAS ne podr\u017eava ovaj na\u010din autentikacije. - -INVALID_REQUEST_PROXY=Parametri 'pgt' i 'targetService' su obavezni. -INVALID_TICKET_SPEC=Ticket nije pro\u0161ao provjeru ispravnosti. Ova pogre\u0161ka mo\u017ee upu\u0107ivati na poku\u0161aj provjere ispravnosti Proxy Ticketa pomo\u0107u Service Ticket validatora ili na neudovoljavanje zahtjevu uz parametar renew=true. - -INVALID_REQUEST=Parametri 'service' i 'ticket' su obavezni -INVALID_TICKET=Ticket ''{0}'' nije prepoznat. -INVALID_SERVICE=Ticket ''{0}'' ne odgovara ovom servisu. Orginalni servis bio je ''{1}'', a isporu\u010deni servis bio je ''{2}''. - -screen.service.error.header=Aplikacija nije autorizirana za uporabu CAS-a -screen.service.error.message=Aplikacia u koju ste se poku\u0161ali prijaviti nije autorizirana za uporabu CAS-a. diff --git a/cas-server-webapp/src/main/resources/messages_it.properties b/cas-server-webapp/src/main/resources/messages_it.properties deleted file mode 100644 index e9857305b3..0000000000 --- a/cas-server-webapp/src/main/resources/messages_it.properties +++ /dev/null @@ -1,103 +0,0 @@ -#Author: Roberto Cosenza http://robcos.com -#Version: $Revision$ $Date$ -#Since: 3.0.5 - -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Benvenuti al Central Authentication Service (CAS). -screen.welcome.security=Per motivi di sicurezza dovresti effettuare il logout e chiudere tutte le finestre del browser quando hai finito di utilizzare servizi che necessitano autenticazione. -screen.welcome.instructions=Inserisci login e password -screen.welcome.label.netid=Login: -screen.welcome.label.netid.accesskey=L -screen.welcome.label.password=Password: -screen.welcome.label.password.accesskey=P -screen.welcome.label.warn=Avvisami prima di autenticarmi su un altro sito -screen.welcome.label.warn.accesskey=A -screen.welcome.button.login=LOGIN -screen.welcome.button.clear=ANNULLA - -# Blocked Errors Page -screen.blocked.header=Accesso Negato -screen.blocked.message=� stata inserita la password sbagliata troppe volte. L'account � stato bloccato. - -#Confirmation Screen Messages -screen.confirmation.message=Clicca qu� per accedere al servizio. - -#Generic Success Screen Messages -screen.success.header=Login eseguito correttamente -screen.success.success=Hai effettuato il login al Central Authentication Service. -screen.success.security=Per motivi di sicurezza dovresti effettuare il logout e chiudere tutte le finestre del browser quando hai finito di utilizzare servizi che necessitano autenticazione. - -#Logout Screen Messages -screen.logout.header=Logout effettuato con successo -screen.logout.success=Hai correttamente effettuato il logout dal Central Authentication Service. -screen.logout.security=Per motivi di sicurezza, si consiglia di chiudere tutte le finestre del browser. -screen.logout.redirect=Puoi rifare il login cliccando qu� - -screen.service.sso.error.header=� necessario effettuare nuovamente l'autenticazione per avere l'accesso a questo servizio -screen.service.sso.error.message=Si � tentato di accedere a un servizio che richiede di effettuare nuovamente l'autenticazione. Si prega di autenticarsi nuovamente. - -error.invalid.loginticket=Ricompila il form dall'inizio senza utilizzare il tasto 'indietro' -required.username=Il campo login � obbligatorio -required.password=Il campo password � obbligatorio -error.authentication.credentials.bad=Login o password errate -error.authentication.credentials.unsupported=Le credenziali utilizzate non sono supportate da CAS - -INVALID_REQUEST_PROXY=I parametri 'pgt' e 'targetService' sono entrambi obbligatori -INVALID_TICKET_SPEC=La convalida del Ticket non ha avuto successo. Una possibile causa di errore potrebbe essere il tentativo di convalidare un Proxy Ticket via un Service Ticket validator. -INVALID_REQUEST=I parametri 'service' e 'ticket' sono entrambi obbligatori -INVALID_TICKET=Il ticket ''{0}'' non � stato riconosciuto -INVALID_SERVICE=Il ticket ''{0}'' non corrisponde a nessun servizio disponibile - -#Service Error Messages -screen.service.error.header=Servizio non autorizzato. -screen.service.error.message=Il servizio a cui stai cercando di accedere non � configurato per CAS - -# LPPE Account Error -screen.accounterror.password.message=La data di rinnovo della password non � specificata, � scaduta o non valida. Si prega di contattare l'amministratore di sistema per recuperare le credenziali di accesso. - -# LPPE Account Disabled -screen.accountdisabled.heading=Questo account � disabilitato. -screen.accountdisabled.message=Si prega di contattare l'amministratore di sistema per recuperare le credenziali di accesso. - -# LPPE Password Expired -screen.expiredpass.heading=La vostra password � scaduta. -screen.expiredpass.message=Si prega di cambiare la password. - -# LPPE Password Must be changed -screen.mustchangepass.heading=La password deve essere cambiata. -screen.mustchangepass.message=Si prega di cambiare la password. - -# LPPE Login out of authorized hours -screen.badhours.heading=Non si � autorizzati a effettuare il login a quest'ora. -screen.badhours.message=Si prega di riprovare pi� tardi. - -# LPPE Login out of authorized workstations -screen.badworkstation.heading=Non si � autorizzati a effettuare il login da questa postazione. -screen.badworkstation.message=Si prega di conttattare l'amministratore di sistema per recuperare le credenziali d'accesso. - -# LPPE Password Warning -screen.warnpass.heading.today=La vostra password scade oggi! -screen.warnpass.heading.tomorrow=La vostra password scade domani! -screen.warnpass.heading.other=La vostra password scade tra {0} giorni. -screen.warnpass.message.line1=Si prega di cambiare la password ora. -screen.warnpass.message.line2=E in corso la redirezione automatica verso la vostra applicazione tra 10 secondi. diff --git a/cas-server-webapp/src/main/resources/messages_ja.properties b/cas-server-webapp/src/main/resources/messages_ja.properties deleted file mode 100644 index a1a5ac0ff2..0000000000 --- a/cas-server-webapp/src/main/resources/messages_ja.properties +++ /dev/null @@ -1,72 +0,0 @@ -#Author: Shoji Kajita -#Version: $Revision$ $Date$ -#Since: 3.1 - -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=\u304a\u3081\u3067\u3068\u3046\u3054\u3056\u3044\u307e\u3059! CAS \u3092\u30aa\u30f3\u30e9\u30a4\u30f3\u306b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3057\u305f\uff0e\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u8a8d\u8a3c\u30cf\u30f3\u30c9\u30e9\u3067\u306f\uff0c\u30e6\u30fc\u30b6\u540d\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u540c\u3058\u3068\u304d\u306b\u8a8d\u8a3c\u3055\u308c\u307e\u3059\uff0e\u305c\u3072\uff0c\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\uff0e -screen.welcome.security=\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u4e0a\u306e\u7406\u7531\u304b\u3089\uff0c\u8a8d\u8a3c\u304c\u5fc5\u8981\u306a\u30b5\u30fc\u30d3\u30b9\u306e\u30a2\u30af\u30bb\u30b9\u7d42\u4e86\u6642\u306b\u306f\uff0c\u30a6\u30a7\u30d6\u30d6\u30e9\u30a6\u30b6\u3092\u30ed\u30b0\u30a2\u30a6\u30c8\u3057\uff0c\u7d42\u4e86\u3057\u3066\u304f\u3060\u3055\u3044\uff0e -screen.welcome.instructions=\u30cd\u30c3\u30c8ID \u304a\u3088\u3073\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044 -screen.welcome.label.netid=\u30cd\u30c3\u30c8ID: -screen.welcome.label.netid.accesskey=n -screen.welcome.label.password=\u30d1\u30b9\u30ef\u30fc\u30c9: -screen.welcome.label.password.accesskey=p -screen.welcome.label.warn=\u4ed6\u306e\u30b5\u30a4\u30c8\u306b\u30ed\u30b0\u30a4\u30f3\u3059\u308b\u524d\u306b\u8b66\u544a\u3092\u51fa\u3059\uff0e -screen.welcome.label.warn.accesskey=w -screen.welcome.button.login=\u30ed\u30b0\u30a4\u30f3 -screen.welcome.button.clear=\u30af\u30ea\u30a2 - -#Confirmation Screen Messages -screen.confirmation.message=\u3053\u3053\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u79fb\u52d5\u3057\u307e\u3059\uff0e - -#Generic Success Screen Messages -screen.success.header=\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3057\u305f -screen.success.success=Central Authentication Service \u306b\u30ed\u30b0\u30a4\u30f3\u3067\u304d\u307e\u3057\u305f\uff0e -screen.success.security=\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u4e0a\u306e\u7406\u7531\u304b\u3089\uff0c\u8a8d\u8a3c\u304c\u5fc5\u8981\u306a\u30b5\u30fc\u30d3\u30b9\u306e\u30a2\u30af\u30bb\u30b9\u7d42\u4e86\u6642\u306b\u306f\uff0c\u30a6\u30a7\u30d6\u30d6\u30e9\u30a6\u30b6\u3092\u30ed\u30b0\u30a2\u30a6\u30c8\u3057\uff0c\u7d42\u4e86\u3057\u3066\u304f\u3060\u3055\u3044\uff0e - -#Logout Screen Messages -screen.logout.header=\u30ed\u30b0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f -screen.logout.success=Central Authentication Service \u3092\u30ed\u30b0\u30a2\u30a6\u30c8\u3067\u304d\u307e\u3057\u305f\uff0e -screen.logout.security=\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u4e0a\u306e\u7406\u7531\u304b\u3089\uff0c\u30a6\u30a7\u30d6\u30d6\u30e9\u30a6\u30b6\u3092\u7d42\u4e86\u3057\u3066\u304f\u3060\u3055\u3044\uff0e -screen.logout.redirect=\u3042\u306a\u305f\u304c\u30a2\u30af\u30bb\u30b9\u3057\u305f\u30b5\u30fc\u30d3\u30b9\u306b\u3088\u308a\u63d0\u4f9b\u3055\u308c\u305f\u30ea\u30f3\u30af\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u3068\u304d\u306f\u3053\u3053\u3092\u30af\u30ea\u30c3\u30af\u3057\u307e\u3059\uff0e - -#Service Error Messages -screen.service.error.header=\u6a29\u9650\u306e\u306a\u3044\u30b5\u30fc\u30d3\u30b9 -screen.service.error.message=\u8a8d\u8a3c\u3057\u3088\u3046\u3068\u3057\u305f\u30b5\u30fc\u30d3\u30b9\u306b\u306f CAS \u3092\u4f7f\u3046\u6a29\u9650\u304c\u3042\u308a\u307e\u305b\u3093\uff0e - -screen.service.sso.error.header=\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u305f\u3081\u306b\u306f\u518d\u8a8d\u8a3c\u304c\u5fc5\u8981 -screen.service.sso.error.message=\u518d\u8a8d\u8a3c\u3092\u8981\u6c42\u3059\u308b\u30b5\u30fc\u30d3\u30b9\u306b\u30a2\u30af\u30bb\u30b9\u3057\u3088\u3046\u3068\u3057\u307e\u3057\u305f\uff0e\u518d\u8a8d\u8a3c\u3092\u8a66\u307f\u3066\u304f\u3060\u3055\u3044\uff0e - -error.invalid.loginticket=\u3059\u3067\u306b\u9001\u4fe1\u6e08\u307f\u306e\u30d5\u30a9\u30fc\u30e0\u306f\u518d\u9001\u4fe1\u3067\u304d\u307e\u305b\u3093\uff0e -required.username=\u30e6\u30fc\u30b6\u540d\u306f\u5fc5\u9808\u30d5\u30a3\u30fc\u30eb\u30c9\u3067\u3059\uff0e -required.password=\u30d1\u30b9\u30ef\u30fc\u30c9\u306f\u5fc5\u9808\u30d5\u30a3\u30fc\u30eb\u30c9\u3067\u3059\uff0e -error.authentication.credentials.bad=\u3042\u306a\u305f\u304c\u5165\u529b\u3057\u305f\u8a8d\u8a3c\u60c5\u5831\u306f\uff0c\u8a8d\u8a3c\u53ef\u80fd\u306a\u3082\u306e\u3067\u3042\u308b\u3053\u3068\u304c\u78ba\u8a8d\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff0e -error.authentication.credentials.unsupported=\u5165\u529b\u3057\u305f\u8a8d\u8a3c\u60c5\u5831\u306f CAS \u3067\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\uff0e - -INVALID_REQUEST_PROXY=\u300cpgt\u300d\u304a\u3088\u3073\u300ctargetService\u300d\u30d1\u30e9\u30e1\u30fc\u30bf\u306e\u4e21\u65b9\u304c\u5fc5\u8981\u3067\u3059 -INVALID_TICKET_SPEC=\u30c1\u30b1\u30c3\u30c8\u306e\u6b63\u5f53\u6027\u57fa\u6e96\u30c1\u30a7\u30c3\u30af\u306b\u5931\u6557\u3057\u307e\u3057\u305f\uff0e\u300c\u30b5\u30fc\u30d3\u30b9\u30c1\u30b1\u30c3\u30c8\u300d\u30d0\u30ea\u30c7\u30fc\u30bf\u306b\u3088\u308b\u300c\u30d7\u30ed\u30af\u30b7\u30c1\u30b1\u30c3\u30c8\u300d\u306e\u6b63\u5f53\u6027\u30c1\u30a7\u30c3\u30af\u3092\u884c\u3063\u305f\u304b\uff0c\u66f4\u65b0\u8981\u6c42\u306e\u898f\u683c\u306b\u3042\u3063\u3066\u3044\u306a\u3044\u30b1\u30fc\u30b9\u304c\u8003\u3048\u3089\u308c\u307e\u3059\uff0e -INVALID_REQUEST=\u300cservice\u300d\u304a\u3088\u3073\u300cticket\u300d\u30d1\u30e9\u30e1\u30fc\u30bf\u306e\u4e21\u65b9\u304c\u5fc5\u8981\u3067\u3059 -INVALID_TICKET=ticket\u300c{0}\u300d\u306f\u8a8d\u8b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -INVALID_SERVICE=ticket\u300c{0}\u300d\u306f\u63d0\u4f9b\u3055\u308c\u3066\u3044\u308b\u30b5\u30fc\u30d3\u30b9\u306b\u4e00\u81f4\u3057\u307e\u305b\u3093 - -screen.service.error.header=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306f CAS \u3092\u4f7f\u3046\u6a29\u9650\u304c\u3042\u308a\u307e\u305b\u3093 -screen.service.error.message=\u8a8d\u8a3c\u3057\u3088\u3046\u3068\u3057\u305f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306f CAS \u3092\u4f7f\u3046\u6a29\u9650\u304c\u3042\u308a\u307e\u305b\u3093\uff0e diff --git a/cas-server-webapp/src/main/resources/messages_mk.properties b/cas-server-webapp/src/main/resources/messages_mk.properties deleted file mode 100644 index 89f726ddb4..0000000000 --- a/cas-server-webapp/src/main/resources/messages_mk.properties +++ /dev/null @@ -1,68 +0,0 @@ -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -# @author \u0412\u0430\u043d\u0433\u0435\u043b \u0410\u0458\u0430\u043d\u043e\u0432\u0441\u043a\u0438 -# \u0418\u043d\u0441\u0442\u0438\u0442\u0443\u0442 \u0437\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u043a\u0430 -# @since 3.1.1 - -#Welcome Screen Messages -screen.welcome.welcome=\u0414\u043e\u0431\u0440\u043e\u0434\u043e\u0458\u0434\u043e\u0432\u0442\u0435 -screen.welcome.security=\u041f\u043e\u0440\u0430\u0434\u0438 \u0441\u0438\u0433\u0443\u0440\u043d\u043e\u0441\u043d\u0438 \u043f\u0440\u0438\u0447\u0438\u043d\u0438 \u0432\u0435 \u043c\u043e\u043b\u0438\u043c\u0435 \u0434\u0430 \u043d\u0435 \u0437\u0430\u0431\u043e\u0440\u0430\u0432\u0438\u0442\u0435 \u0434\u0430 \u0441\u0435 \u043e\u0434\u0458\u0430\u0432\u0438\u0442\u0435 \u0438 \u0434\u0430 \u0433\u043e \u0437\u0430\u0442\u0432\u043e\u0440\u0438\u0442\u0435 \u0432\u0430\u0448\u0438\u043e\u0442 \u043f\u0440\u0435\u0431\u0430\u0440\u0443\u0432\u0430\u0447 \u043f\u043e \u0437\u0430\u0432\u0440\u0448\u0443\u0432\u0430\u045a\u0435\u0442\u043e \u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u0430 \u0441\u043e \u0430\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438\u0442\u0435. -screen.welcome.instructions=\u0412\u043d\u0435\u0441\u0435\u0442\u0435 \u043a\u043e\u0440\u0438\u0441\u043d\u0438\u0447\u043a\u043e \u0438\u043c\u0435 \u0438 \u043b\u043e\u0437\u0438\u043d\u043a\u0430. -screen.welcome.label.netid=\u041a\u043e\u0440\u0438\u0441\u043d\u0438\u0447\u043a\u043e \u0438\u043c\u0435: -screen.welcome.label.netid.accesskey=\u043a -screen.welcome.label.password=\u041b\u043e\u0437\u0438\u043d\u043a\u0430: -screen.welcome.label.password.accesskey=\u043b -screen.welcome.label.warn=\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0434\u0438 \u043c\u0435 \u043f\u0440\u0438 \u043d\u0430\u0458\u0430\u0432\u0443\u0432\u0430\u045a\u0435 \u0432\u043e \u0434\u0440\u0443\u0433\u0438 \u0430\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438. -screen.welcome.label.warn.accesskey=\u043f -screen.welcome.button.login=\u041d\u0410\u0408\u0410\u0412\u0410 -screen.welcome.button.clear=\u041f\u041e\u041d\u0418\u0428\u0422\u0418 - -#Confirmation Screen Messages -screen.confirmation.message=\u041f\u0440\u0438\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \u0442\u0443\u043a\u0430 \u0437\u0430 \u0432\u043b\u0435\u0437 \u0432\u043e \u0430\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0458\u0430\u0442\u0430. - -#Generic Success Screen Messages -screen.success.header=\u0423\u0441\u043f\u0435\u0448\u043d\u0430 \u043d\u0430\u0458\u0430\u0432\u0430 -screen.success.success=\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0435 \u043d\u0430\u0458\u0430\u0432\u0438\u0432\u0442\u0435 \u043d\u0430 \u0426\u0435\u043d\u0442\u0440\u0430\u043b\u043d\u0438\u043e\u0442 \u0410\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u0441\u043a\u0438 \u0421\u0435\u0440\u0432\u0438\u0441. -screen.success.security=\u041f\u043e\u0440\u0430\u0434\u0438 \u0441\u0438\u0433\u0443\u0440\u043d\u043e\u0441\u043d\u0438 \u043f\u0440\u0438\u0447\u0438\u043d\u0438 \u0432\u0435 \u043c\u043e\u043b\u0438\u043c\u0435 \u0434\u0430 \u0441\u0435 \u043e\u0434\u0458\u0430\u0432\u0438\u0442\u0435 \u0438 \u0434\u0430 \u0433\u043e \u0437\u0430\u0442\u0432\u043e\u0440\u0438\u0442\u0435 \u0432\u0430\u0448\u0438\u043e\u0442 browser \u043f\u043e \u0437\u0430\u0432\u0440\u0448\u0443\u0432\u0430\u045a\u0435\u0442\u043e \u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u0430 \u0441\u043e \u0430\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438\u0442\u0435. - -#Logout Screen Messages -screen.logout.header=\u0423\u0441\u043f\u0435\u0448\u043d\u0430 \u043e\u0434\u0458\u0430\u0432\u0430 -screen.logout.success=\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0435 \u043e\u0434\u0458\u0430\u0432\u0438\u0432\u0442\u0435 \u043e\u0434 \u0426\u0435\u043d\u0442\u0440\u0430\u043b\u043d\u0438\u043e\u0442 \u0410\u0432\u0442\u0435\u0442\u0438\u043a\u0430\u0446\u0438\u0441\u043a\u0438 \u0421\u0435\u0440\u0432\u0438\u0441. -screen.logout.security=\u041f\u043e\u0440\u0430\u0434\u0438 \u0441\u0438\u0433\u0443\u0440\u043d\u043e\u0441\u043d\u0438 \u043f\u0440\u0438\u0447\u0438\u043d\u0438 \u0432\u0435 \u043c\u043e\u043b\u0438\u043c\u0435 \u0434\u0430 \u0433\u043e \u0437\u0430\u0442\u0432\u043e\u0440\u0438\u0442\u0435 \u0432\u0430\u0448\u0438\u043e\u0442 browser. -screen.logout.redirect=\u0421\u0435\u0440\u0432\u0438\u0441\u043e\u0442 \u043a\u043e\u0458 \u0432\u0435 \u0434\u043e\u043d\u0435\u0441\u0435 \u043d\u0430 \u0426\u0410\u0421 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0438\u0442\u0435, \u043d\u0443\u0434\u0438 \u043b\u0438\u043d\u043a \u043d\u0430 \u043a\u043e\u0458 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435 \u043d\u0430\u0442\u0430\u043c\u0443 \u0441\u043e \u043f\u0440\u0438\u0442\u0438\u0441\u043a\u0430\u045a\u0435 \u0422\u0423\u041a\u0410. - -screen.service.sso.error.header=\u0417\u0430 \u043f\u0440\u0438\u0441\u0442\u0430\u043f \u043d\u0430 \u043e\u0432\u043e\u0458 \u0441\u0435\u0440\u0432\u0438\u0441 \u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u0430 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u0458\u0430 -screen.service.sso.error.message=\u0421\u0435 \u043e\u0431\u0438\u0434\u043e\u0432\u0442\u0435 \u0434\u0430 \u043f\u0440\u0438\u0441\u0442\u0430\u043f\u0438\u0442\u0435 \u043d\u0430 \u0441\u0435\u0440\u0432\u0438\u0441 \u043a\u043e\u0458 \u043f\u043e\u0431\u0430\u0440\u0443\u0432\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u0458\u0430, \u043f\u0440\u0438\u0442\u043e\u0430 \u043d\u0435 \u0441\u0442\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0446\u0438\u0440\u0430\u043d\u0438. \u0412\u0435 \u043c\u043e\u043b\u0438\u043c\u0435 \u0434\u0430 \u0441\u0435 \u043e\u0431\u0438\u0434\u0435\u0442\u0435 \u0434\u0430 \u0441\u0435 \u0430\u0432\u0442\u0435\u0442\u0438\u043d\u0446\u0438\u0440\u0430\u0442\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0441\u043e \u043f\u0440\u0438\u0442\u0438\u0441\u043a\u0430\u045a\u0435 \u0422\u0423\u041a\u0410. - -error.invalid.loginticket=\u0421\u043e\u0434\u0440\u0436\u0438\u043d\u0430\u0442\u0430 \u043d\u0430 \u0444\u043e\u0440\u043c\u0443\u043b\u0430\u0440\u043e\u0442 \u0435 \u0432\u0435\u045c\u0435 \u0438\u0441\u043f\u0440\u0430\u0442\u0435\u043d\u0430. \u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0438\u0441\u043f\u0440\u0430\u045c\u0430\u045a\u0435 \u043d\u0435 \u0435 \u0434\u043e\u0437\u0432\u043e\u043b\u0435\u043d\u043e. -required.username=\u041a\u043e\u0440\u0438\u0441\u043d\u0438\u0447\u043a\u043e\u0442\u043e \u0438\u043c\u0435 \u0437\u0430\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u0442\u0440\u0435\u0431\u0430 \u0434\u0430 \u0441\u0435 \u043f\u043e\u043f\u043e\u043b\u043d\u0438. -required.password=\u041b\u043e\u0437\u0438\u043d\u043a\u0430\u0442\u0430 \u0437\u0430\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e \u0442\u0440\u0435\u0431\u0430 \u0434\u0430 \u0441\u0435 \u043f\u043e\u043f\u043e\u043b\u043d\u0438 -error.authentication.credentials.bad=\u041a\u043e\u0440\u0438\u0441\u043d\u0438\u0447\u043a\u043e\u0442\u043e \u0438\u043c\u0435 \u0438/\u0438\u043b\u0438 \u043b\u043e\u0437\u0438\u043d\u043a\u0430\u0442\u0430 \u043d\u0435 \u0441\u0435 \u0438\u0441\u043f\u0440\u0430\u0432\u043d\u0438. -error.authentication.credentials.unsupported=\u0426\u0410\u0421 \u043d\u0435 \u0433\u043e \u043f\u043e\u0434\u0434\u0440\u0436\u0443\u0432\u0430 \u043e\u0432\u043e\u0458 \u043d\u0430\u0447\u0438\u043d \u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u0458\u0430. - -INVALID_REQUEST_PROXY=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0442\u0435 'pgt' \u0438 'targetService' \u0441\u0435 \u0437\u0430\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u0438. -INVALID_TICKET_SPEC=\u0411\u0438\u043b\u0435\u0442\u043e\u0442 \u043d\u0435 \u0458\u0430 \u043f\u043e\u043c\u0438\u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430\u0442\u0430 \u043d\u0430 \u0438\u0441\u043f\u0440\u0430\u0432\u043d\u043e\u0441\u0442. \u041e\u0432\u0430\u0430 \u0433\u0440\u0435\u0448\u043a\u0430 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0443\u043f\u0430\u0442\u0443\u0432\u0430 \u043d\u0430 \u043e\u0431\u0438\u0434 \u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0438\u0441\u043f\u0440\u0430\u0432\u043d\u043e\u0441\u0442 \u043d\u0430 \u041f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u0411\u0438\u043b\u0435\u0442 \u0441\u043e \u043f\u043e\u043c\u043e\u0448 \u043d\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440 \u043d\u0430 \u0421\u0435\u0440\u0432\u0438\u0441\u0435\u043d \u0411\u0438\u043b\u0435\u0442 \u0438\u043b\u0438 \u043d\u0430 \u043d\u0435\u0437\u0430\u0434\u043e\u0432\u043e\u043b\u0443\u0432\u0430\u045a\u0435 \u043d\u0430 \u0431\u0430\u0440\u0430\u045a\u0430\u0442\u0430 \u0441\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0430\u0440\u043e\u0442 renew=true. - -INVALID_REQUEST=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0442\u0435 'service' \u0438 'ticket' \u0441\u0435 \u0437\u0430\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u0438. -INVALID_TICKET=\u0411\u0438\u043b\u0435\u0442\u043e\u0442 ''{0}'' \u043d\u0435 \u0435 \u043f\u0440\u0435\u043f\u043e\u0437\u043d\u0430\u0442. -INVALID_SERVICE=\u0411\u0438\u043b\u0435\u0442\u043e\u0442 ''{0}'' \u043d\u0435 \u043e\u0434\u0433\u043e\u0432\u0430\u0440\u0430 \u043d\u0430 \u043e\u0432\u043e\u0458 \u0441\u0435\u0440\u0432\u0438\u0441. \u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u043d\u0438\u043e\u0442 \u0441\u0435\u0440\u0432\u0438\u0441 \u0431\u0435\u0448\u0435 ''{1}'', \u0430 \u0438\u0441\u043f\u043e\u0440\u0430\u0447\u0430\u043d\u0438\u043e\u0442 \u0441\u0435\u0440\u0432\u0438\u0441 \u0435 ''{2}''. - -screen.service.error.header=\u0410\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0458\u0430\u0442\u0430 \u043d\u0435 \u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0438\u0440\u0430\u043d\u0430 \u0437\u0430 \u043a\u043e\u0440\u0438\u0441\u0442\u0435\u045a\u0435 \u043d\u0430 \u0426\u0410\u0421 -screen.service.error.message=\u0410\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0458\u0430\u0442\u0430 \u0432\u043e \u043a\u043e\u0458\u0430 \u0441\u0435 \u043e\u0431\u0438\u0434\u0443\u0432\u0430\u0442\u0435 \u0434\u0430 \u0441\u0435 \u043d\u0430\u0458\u0430\u0432\u0438\u0442\u0435 \u043d\u0435 \u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0438\u0440\u0430\u043d\u0430 \u0437\u0430 \u043a\u043e\u0440\u0438\u0441\u0442\u0435\u045a\u0435 \u043d\u0430 \u0426\u0410\u0421. diff --git a/cas-server-webapp/src/main/resources/messages_nl.properties b/cas-server-webapp/src/main/resources/messages_nl.properties deleted file mode 100644 index eb70ca24c9..0000000000 --- a/cas-server-webapp/src/main/resources/messages_nl.properties +++ /dev/null @@ -1,65 +0,0 @@ -#Author: Jan "Velpi" Van der Velpen -#Version $Revision$ $Date$ -#Since 3.0.3 - -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Proficiat met de succesvolle installatie van CAS! Met de standaard "authentication handler" kan je ingeloggen als de gebruikersnaam gelijk is aan het wachtwoord. Je kan het nu proberen. -screen.welcome.security=Voor de veiligheid moet je uitloggen en je browser sluiten wanneer je geen toegang meer nodig hebt tot afgeschermde applicaties! -screen.welcome.instructions=Om verder te gaan dien je jezelf te authenticeren. -screen.welcome.label.netid.accesskey=g -screen.welcome.label.netid=Gebruikersnaam: -screen.welcome.label.password=Wachtwoord: -screen.welcome.label.password.accesskey=w -screen.welcome.label.warn=Vraag toestemming vooraleer me ingelogd door te sturen naar andere sites. -screen.welcome.label.warn.accesskey=v -screen.welcome.button.login=LOGIN -screen.welcome.button.clear=CLEAR - -#Confirmation Screen Messages -screen.confirmation.message=Doorgaan naar de applicatie. - -#Generic Success Screen Messages -screen.success.header=Succesvol ingelogd. -screen.success.success=Je bent ingelogd bij de Central Authentication Service. -screen.success.security=Voor de veiligheid moet je uitloggen en je browser sluiten wanneer je geen toegang meer nodig hebt tot afgeschermde applicaties! - -#Logout Screen Messages -screen.logout.header=Succesvol uitgelogd. -screen.logout.success=Je bent nu uitgelogd bij de Central Authentication Service. -screen.logout.security=Voor de veiligheid dien je je browser nu af te sluiten. -screen.logout.redirect=De applicatie waar je vandaan komt heeft deze link opgegeven die je kan volgen door hier te klikken. - -error.invalid.loginticket=Je mag geen formulier verzenden dat je al eens hebt verzonden. -required.username=Gelieve een gebruikersnaam in te vullen. -required.password=Gelieve een wachtwoord in te vullen. -error.authentication.credentials.bad=De combinatie van gebruikersnaam en wachtwoord was niet juist. -error.authentication.credentials.unsupported=De verstuurde identificatiegegevens worden niet ondersteund door CAS. - -INVALID_REQUEST_PROXY='pgt' en 'targetService' zijn verplichte parameters. -INVALID_TICKET_SPEC=Het ticket kwam niet overeen met de specificatie voor validatie. Misschien probeer je een Proxy Ticket te valideren op de Service Ticket validator, of komt "renew true" niet overeen. -INVALID_REQUEST='service' en 'ticket' zijn verplichte parameters. -INVALID_TICKET=ticket ''{0}'' is niet gekend. -INVALID_SERVICE=ticket ''{0}'' komt niet overeen met de opgegeven service. - -screen.service.error.header=Geen toegang. -screen.service.error.message=De applicatie waarvoor je toegang vroeg heeft geen toestemming om deze CAS te gebruiken. diff --git a/cas-server-webapp/src/main/resources/messages_pl.properties b/cas-server-webapp/src/main/resources/messages_pl.properties deleted file mode 100644 index a0e37bbd83..0000000000 --- a/cas-server-webapp/src/main/resources/messages_pl.properties +++ /dev/null @@ -1,96 +0,0 @@ -# @author Maja Gorecka-Wolniewicz -# @since 3.1.1 - -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Gratulujemy, us\u0142uga CAS jest gotowa do dzia\u0142ania! Domy\u015blny tryb uwierzytelniania akceptuje dane, w kt\u00f3rych has\u0142o jest takie samo jak nazwa u\u017cytkownika - spr\u00f3buj! -screen.welcome.security=Dla zachowania bezpiecze\u0144stwa, gdy zako\u0144czysz korzystanie z us\u0142ug wymagaj\u0105cych uwierzytelnienia, wyloguj si\u0119 i zamknij przegl\u0105dark\u0119! -screen.welcome.instructions=Wprowad\u017a sw\u00f3j identyfikator sieciowy i has\u0142o -screen.welcome.label.netid=Identyfikator: -screen.welcome.label.netid.accesskey=i -screen.welcome.label.password=Has\u0142o: -screen.welcome.label.password.accesskey=h -screen.welcome.label.warn=Ostrzegaj mnie przed zalogowaniem na innych serwerach. -screen.welcome.label.warn.accesskey=o -screen.welcome.button.login=ZALOGUJ -screen.welcome.button.clear=WYCZY\u015a\u0106 - -#Confirmation Screen Messages -screen.confirmation.message=Naci\u015bnij tutaj, by przej\u015bc do aplikacji. - -#Generic Success Screen Messages -screen.success.header=Udane logowanie -screen.success.success=Zalogowa\u0142e\u015b si\u0119 w CAS - Centralnej Us\u0142udze Uwierzytelniania. -screen.success.security=Dla zachowania bezpiecze\u0144stwa, gdy zako\u0144czysz korzystanie z us\u0142ug wymagaj\u0105cych uwierzytelnienia, wyloguj si\u0119 i zamknij przegl\u0105dark\u0119! - -#Logout Screen Messages -screen.logout.header=Udane wylogowanie -screen.logout.success=Wylogowa\u0142e\u015b si\u0119 z CAS - Centralnej Us\u0142ugi Uwierzytelniania. -screen.logout.security=Dla zachowania bezpiecze\u0144stwa zamknij przegl\u0105dark\u0119. -screen.logout.redirect=Us\u0142uga przekaza\u0142a adres, do kt\u00f3rego przejdziesz naciskaj\u0105c tutaj. - -screen.service.sso.error.header=W celu dost\u0119pu do us\u0142ugi wymagane jest ponowne uwierzytelnienie -screen.service.sso.error.message=Pr\u00f3ba dost\u0119pu do us\u0142ugi, kt\u00f3ra wymaga ponownego uwierzytelnienia. Pon\u00f3w uwierzytelnienie. - -error.invalid.loginticket=Nie mo\u017cesz ponownie wys\u0142a\u0107 formularza wcze\u015bniej wys\u0142anego. -required.username=Nazwa u\u017cytkownika jest polem wymaganym. -required.password=Has\u0142o jest polem wymaganym. -error.authentication.credentials.bad=Dostarczone dane uwierzytelniania nie mog\u0105 zosta\u0107 uznane za poprawne. -error.authentication.credentials.unsupported=Dostarczone dane uwierzytelniania nie nie s\u0105 akceptowane przez us\u0142ug\u0119 CAS. - -INVALID_REQUEST_PROXY=parametry 'pgt' i 'targetService' s\u0105 wymagane -INVALID_TICKET_SPEC=Bilet niezgodny ze specyfikacj\u0105. Mo\u017cliwe przyczyny b\u0142\u0119du to pr\u00f3ba sprawdzenia biletu proxy za pomoc\u0105 walidatora biletu us\u0142ugi, lub brak zgodno\u015bci ze zleceniem odnowienia uwierzytelnienia. -INVALID_REQUEST=parametry 'service' i 'ticket' s\u0105 wymagane -INVALID_TICKET=nieznana posta\u0107 biletu ''{0}'' -INVALID_SERVICE=Bilet ''{0}'' nie nale\u017cy do tej us\u0142ugi. Oryginalna us\u0142uga to ''{1}'', aktualna us\u0142uga to ''{2}''. - -screen.service.error.header=Brak uprawnie\u0144 do korzystania z CAS -screen.service.error.message=Aplikacja, w kt\u00f3ej chcia\u0142e\u015b zosta\u0107 uwierzytelniony nie ma uprawnie\u0144 do korzystania z CAS. -# LPPE Account Error -screen.accounterror.password.message=Termin wa\u017cno\u015bci has\u0142a up\u0142yn\u0105\u0142, nie jest okre\u015blony, albo ustawiona data jest nieprawid\u0142owa. Prosz\u0119 skontaktowa\u0107 si\u0119 z pomoc\u0105 techniczn\u0105 w celu uzyskania dost\u0119pu. - -# LPPE Account Disabled -screen.accountdisabled.heading=Konto zosta\u0142o zablokowane. -screen.accountdisabled.message=Prosz\u0119 skontaktowa\u0107 si\u0119 z pomoc\u0105 techniczn\u0105 w celu uzyskania dost\u0119pu. - -# LPPE Password Expired -screen.expiredpass.heading=Termin wa\u017cno\u015bci has\u0142a up\u0142yn\u0105\u0142. -screen.expiredpass.message=Prosz\u0119 zmieni\u0107 has\u0142o. - -# LPPE Password Must be changed -screen.mustchangepass.heading=Musisz zmieni\u0107 has\u0142o. -screen.mustchangepass.message=Prosz\u0119 zmieni\u0107 has\u0142o. - -# LPPE Login out of authorized hours -screen.badhours.heading=Nie mo\u017cesz zalogowa\u0107 si\u0119 w tym czasie. -screen.badhours.message=Prosz\u0119 spr\u00f3bowa\u0107 p\u00f3\u017aniej. - -# LPPE Login out of authorized workstations -screen.badworkstation.heading=Nie mo\u017cesz zalogowa\u0107 si\u0119 z tego komputera. -screen.badworkstation.message=Prosz\u0119 skontaktowa\u0107 si\u0119 z pomoc\u0105 techniczn\u0105 w celu uzyskania dost\u0119pu. - -# LPPE Password Warning -screen.warnpass.heading.today=Dzisiaj up\u0142ywa termin wa\u017cno\u015bci Twojego has\u0142a! -screen.warnpass.heading.tomorrow=Jutro up\u0142ywa termin wa\u017cno\u015bci Twojego has\u0142a! -screen.warnpass.heading.other=Termin wa\u017cno\u015bci Twojego has\u0142a wygasa za {0} dni. -screen.warnpass.message.line1=Prosz\u0119 natychmiast zmieni\u0107 has\u0142o. -screen.warnpass.message.line2=Za 10 sekund nast\u0105pi przekierowanie to wybranej aplikacji. diff --git a/cas-server-webapp/src/main/resources/messages_pt_BR.properties b/cas-server-webapp/src/main/resources/messages_pt_BR.properties deleted file mode 100644 index 8ffb9649b7..0000000000 --- a/cas-server-webapp/src/main/resources/messages_pt_BR.properties +++ /dev/null @@ -1,64 +0,0 @@ -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Parab\u00e9ns por colocar o CAS no ar! O autenticador padr\u00e3o usa Nome de Usu\u00e1rio igual a Senha: v\u00e1 em frente e tente! -screen.welcome.security=Por raz\u00f5es de seguran\u00e7a, por favor deslogue e feche o seu navegador quando terminar de acessar os servi\u00e7os que precisam de autentica\u00e7\u00e3o! -screen.welcome.instructions=Entre com seu usu\u00e1rio e Senha -screen.welcome.label.netid=Usu\u00e1rio: -screen.welcome.label.netid.accesskey=u -screen.welcome.label.password=Senha: -screen.welcome.label.password.accesskey=s -screen.welcome.label.warn=Avisar anter de logar em outros sites. -screen.welcome.label.warn.accesskey=a -screen.welcome.button.login=ENTRAR -screen.welcome.button.clear=LIMPAR - -#Confirmation Screen Messages -screen.confirmation.message=Clique aqui para ir para a aplica\u00e7\u00e3o. - -#Generic Success Screen Messages -screen.success.header=Sucesso ao se logar -screen.success.success=Voc\u00ea se logou com sucesso no Servi\u00e7o de Autentica\u00e7\u00e3o Central. -screen.success.security=Por raz\u00f5es de seguran\u00e7a, por favor efetue um Logout e feche seu navegador quando voc\u00ea terminar de acessar os servi\u00e7os que precisam de autentica\u00e7\u00e3o! - -#Logout Screen Messages -screen.logout.header=Sucesso ao se deslogar -screen.logout.success=Voc\u00ea se deslogou com sucesso no Servi\u00e7o de Autentica\u00e7\u00e3o Central. -screen.logout.security=Por raz\u00f5es de seguran\u00e7a, feche o seu navegador. -screen.logout.redirect=O servi\u00e7o de onde voc\u00ea veio fornecer um link que voc\u00ea pode seguir clicando aqui. - -screen.service.sso.error.header=Re-Autenti\u00e7\u00e3o Obrigat\u00f3ria para Acessar esse Servi\u00e7o -screen.service.sso.error.message=Voc\u00ea tentou acessar um servi\u00e7o que necessita de autentica\u00e7\u00e3o sem re-autentica\u00e7\u00e3o. Por favor, tente autenticar novamente. - -error.invalid.loginticket=Voc\u00ea n\u00e3o pode tentar re-enviar um formul\u00e1rio que j\u00e1 vou enviado anteriormente. -required.username=Usu\u00e1rio \u00e9 um campo obrigat\u00f3rio. -required.password=Senha \u00e9 um campo obrigat\u00f3rio. -error.authentication.credentials.bad=Usu\u00e1rio ou senha inv\u00e1lidos. -error.authentication.credentials.unsupported=As credenciais fornecidas n\u00e3o n\u00e3o suportadas pelo CAS. - -INVALID_REQUEST_PROXY='pgt' e 'targetService' s\u00e3o par\u00e2metros obrigat\u00f3rios -INVALID_TICKET_SPEC=O Ticket falhou a valida\u00e7\u00e3o da especifica\u00e7\u00e3o. Possiveis erros incluem tentativa de validar um Proxy Ticket por meio de um validador Service Ticket, ou n\u00e3o estar de acordo com o pedido de renova\u00e7\u00e3o. -INVALID_REQUEST='service' e 'ticket' s\u00e3o par\u00e2metros obrigat\u00f3rios -INVALID_TICKET=ticket ''{0}'' n\u00e3o reconhecido -INVALID_SERVICE=ticket ''{0}'' n\u00e3o casa com o servi\u00e7o fornecido. O servi\u00e7o original era ''{1}'' e o servi\u00e7o fornecido era ''{2}''. - -screen.service.error.header=Aplica\u00e7\u00e3o n\u00e3o Autorizada a usar o CAS -screen.service.error.message=A aplica\u00e7\u00e3o que voc\u00ea tentou autenticar n\u00e3o \u00e9 autorizada a usar o CAS. diff --git a/cas-server-webapp/src/main/resources/messages_pt_PT.properties b/cas-server-webapp/src/main/resources/messages_pt_PT.properties deleted file mode 100644 index 001f99c09e..0000000000 --- a/cas-server-webapp/src/main/resources/messages_pt_PT.properties +++ /dev/null @@ -1,69 +0,0 @@ -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Parab\u00e9ns! O CAS est\u00e1 agora online! O autenticador padr\u00e3o usa o nome de utilizador igual \u221a\u2020 palavra-passe: v\u00e1 em frente e experimente! -screen.welcome.security=Por quest\u00f5es de seguran\u00e7a, por favor feche o seu browser quando terminar de aceder aos servi\u00e7os que necessitam de autentica\u00e7\u00e3o! -screen.welcome.instructions=Insira o seu utilizador e respectiva palavra-passe -screen.welcome.label.netid=Utilizador: -screen.welcome.label.netid.accesskey=u -screen.welcome.label.password=Palavra-passe: -screen.welcome.label.password.accesskey=p -screen.welcome.label.warn=Avise-me antes de entrar noutros sites. -screen.welcome.label.warn.accesskey=A -screen.welcome.button.login=ENTRAR -screen.welcome.button.clear=LIMPAR - -# Blocked Errors Page -screen.blocked.header=Accesso Bloqueado -screen.blocked.message=Inseriu a palavra-chave incorrectamente demasiadas vezes. A sua conta foi bloqueada. - -#Confirmation Screen Messages -screen.confirmation.message=Clique aqui para ir para a aplica\u00e7\u00e3o. - -#Generic Success Screen Messages -screen.success.header=Sess\u00e3o iniciada com sucesso. -screen.success.success=A sua sess\u00e3o no Servi\u00e7o de Autentica\u00e7\u00e3o Central foi iniciada com sucesso. -screen.success.security=Por raz\u00f5es de seguran\u00e7a, por favor fa\u00e7a Logout e feche o seu browser quando terminar de aceder aos servi\u00e7os que necessitam de autentica\u00e7\u00e3o! - - -#Logout Screen Messages -screen.logout.header=Sess\u00e3o terminada com sucesso. -screen.logout.success=A sua sess\u00e3o no Servi\u00e7o de Autentica\u00e7\u00e3o Central foi terminada com sucesso. -screen.logout.security=Por raz\u00f5es de seguran\u00e7a, por favor feche o seu browser. -screen.logout.redirect=O servi\u00e7o de origem providenciou um link que pode ser seguido ao clicar aqui. - -screen.service.sso.error.header=\u221a\u00e2 necess\u221a\u00b0ria reautentica\u221a\u00df\u221a\u00a3o para aceder a este servi\u221a\u00dfo -screen.service.sso.error.message=Voc\u221a\u2122 tentou o acesso a um servi\u221a\u00dfo que requer reautentica\u221a\u00df\u221a\u00a3o sem a efectuar. Por favor tente autenticar-se novamente. - -error.invalid.loginticket=N\u221a\u00a3o pode tentar reenviar um formul\u221a\u00b0rio que foi enviado anteriormente. -required.username=Utilizador \u221a\u00a9 um campo obrigat\u221a\u2265rio. -required.password=Palavra-passe \u221a\u00a9 um campo obrigat\u221a\u2265rio. -error.authentication.credentials.bad=Utilizador ou palavra-passe inv\u221a\u00b0lidos. -error.authentication.credentials.unsupported=As credenciais fornecidas n\u221a\u00a3o s\u221a\u00a3o suportadas pelo Servi\u221a\u00dfo de Autentica\u221a\u00df\u221a\u00a3o Central. - -INVALID_REQUEST_PROXY=Os par\u221a\u00a2metros 'pgt' e 'targetService' s\u221a\u00a3o obrigat\u221a\u2265rios -INVALID_TICKET_SPEC=O Ticket falhou a valida\u221a\u00df\u221a\u00a3o de especifica\u221a\u00df\u221a\u00a3o. Poder\u221a\u00a3o ser causas a tentativa de validar um Proxy Ticket atr\u221a\u00b0v\u221a\u00a9s de um validador Service Ticket ou n\u221a\u00a3o estar de acordo com o pedido de renova\u221a\u00df\u221a\u00a3o. -INVALID_REQUEST=Os par\u221a\u00a2metros 'service' e 'ticket' s\u221a\u00a3o obrigat\u221a\u2265rios -INVALID_TICKET=ticket ''{0}'' n\u221a\u00a3o reconhecido -INVALID_SERVICE=ticket ''{0}'' n\u221a\u00a3o coincide com o servi\u221a\u00dfo fornecido. O servi\u221a\u00dfo original foi ''{1}'' e o servi\u221a\u00dfo fornecido foi ''{2}''. - -screen.service.error.header=Aplica\u221a\u00df\u221a\u00a3o n\u221a\u00a3o autorizada a usar o Servi\u221a\u00dfo de Autentica\u221a\u00df\u221a\u00a3o Central -screen.service.error.message=A aplica\u221a\u00df\u221a\u00a3o onde se tentou autenticar n\u221a\u00a3o est\u221a\u00b0 autorizada a usar o Servi\u221a\u00dfo de Autentica\u221a\u00df\u221a\u00a3o Central diff --git a/cas-server-webapp/src/main/resources/messages_ru.properties b/cas-server-webapp/src/main/resources/messages_ru.properties deleted file mode 100644 index 88a1890a4e..0000000000 --- a/cas-server-webapp/src/main/resources/messages_ru.properties +++ /dev/null @@ -1,107 +0,0 @@ -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0441 \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u044b CAS! "Authentication handler", \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438 \u0432 \u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0435\u0441\u043b\u0438 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u044e\u0442: \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 CAS \u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438. -screen.welcome.security=\u0412 \u0446\u0435\u043b\u044f\u0445 \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u044b\u0439\u0434\u0438\u0442\u0435 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0437\u0430\u043a\u0440\u043e\u0439\u0442\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440, \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0432 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438! -screen.welcome.instructions=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c -screen.welcome.label.netid=\u041b\u043e\u0433\u0438\u043d: -screen.welcome.label.netid.accesskey=\u043b -screen.welcome.label.password=\u041f\u0430\u0440\u043e\u043b\u044c: -screen.welcome.label.password.accesskey=\u043f -screen.welcome.label.warn=\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0434\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u0434 \u0432\u0445\u043e\u0434\u043e\u043c \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0435 \u0441\u0430\u0439\u0442\u044b. -screen.welcome.label.warn.accesskey=\u0440 -screen.welcome.button.login=\u0412\u041e\u0419\u0422\u0418 -screen.welcome.button.clear=\u041e\u0427\u0418\u0421\u0422\u0418\u0422\u042c - -logo.title=\u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043d\u0430 \u0434\u043e\u043c\u0430\u0448\u043d\u044e\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 Jasig -copyright=Copyright © 2005–2012 Jasig, Inc. \u0412\u0441\u0435 \u043f\u0440\u0430\u0432\u0430 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u044b. - -# Blocked Errors Page -screen.blocked.header=\u0421\u0435\u0440\u0432\u0438\u0441 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430. -screen.blocked.message=\u0412\u044b \u0432\u0432\u0435\u043b\u0438 \u043d\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u043b\u043e\u0433\u0438\u043d \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0437. \u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u043f\u0435\u0440\u0435\u043a\u0440\u044b\u0442. - -#Confirmation Screen Messages -screen.confirmation.message=\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u0441\u044e\u0434\u0430 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. - -#Generic Success Screen Messages -screen.success.header=\u0412\u0445\u043e\u0434 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0443\u0441\u043f\u0435\u0448\u0435\u043d. -screen.success.success=\u0412\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u043e\u0448\u043b\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 Central Authentication Service. -screen.success.security=\u0412 \u0446\u0435\u043b\u044f\u0445 \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u044b\u0439\u0434\u0438\u0442\u0435 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0437\u0430\u043a\u0440\u043e\u0439\u0442\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440, \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0432 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438! - -#Logout Screen Messages -screen.logout.header=\u0412\u044b\u0445\u043e\u0434 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0443\u0441\u043f\u0435\u0448\u0435\u043d. -screen.logout.success=\u0412\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u044b\u0448\u043b\u0438 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b Central Authentication Service. -screen.logout.security=\u0412 \u0446\u0435\u043b\u044f\u0445 \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438, \u0437\u0430\u043a\u0440\u043e\u0439\u0442\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440. -screen.logout.redirect=\u0421\u0435\u0440\u0432\u0438\u0441, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0434\u043e\u0441\u0442\u0443\u043f, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u043b \u0441\u0441\u044b\u043b\u043a\u0443 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430. - -screen.service.sso.error.header=\u041f\u0435\u0440\u0435\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443. -screen.service.sso.error.message=\u0412\u044b \u043f\u043e\u043f\u044b\u0442\u0430\u043b\u0438\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0435\u0440\u0435\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u044d\u0442\u043e \u0441\u043d\u043e\u0432\u0430. - -error.invalid.loginticket=\u0412\u0435\u0431-\u0444\u043e\u0440\u043c\u0430 \u0443\u0436\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u043f\u0435\u0440\u0435\u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430. -required.username=\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f - \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435 \u0432\u0432\u043e\u0434\u0430. -required.password=\u041f\u0430\u0440\u043e\u043b\u044c - \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435 \u0432\u0432\u043e\u0434\u0430. - -# Authentication failure messages -authenticationFailure.AccountDisabledException=\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f. -authenticationFailure.AccountLockedException=\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0430. -authenticationFailure.CredentialExpiredException=\u0412\u0430\u0448 \u043f\u0430\u0440\u043e\u043b\u044c \u043f\u0440\u043e\u0441\u0440\u043e\u0447\u0435\u043d. -authenticationFailure.InvalidLoginLocationException=\u0412\u0445\u043e\u0434 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0438\u0437 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430 \u0437\u0430\u043f\u0440\u0435\u0449\u0451\u043d. -authenticationFailure.InvalidLoginTimeException=\u0412\u0445\u043e\u0434 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0432 \u0434\u0430\u043d\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u0443\u0442\u043e\u043a \u0437\u0430\u043f\u0440\u0435\u0449\u0451\u043d. -authenticationFailure.AccountNotFoundException=\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. -authenticationFailure.FailedLoginException=\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. -authenticationFailure.UNKNOWN=\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. - -INVALID_REQUEST_PROXY=\u041e\u0431\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 'pgt' \u0438 'targetService' \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b. -INVALID_TICKET_SPEC="Ticket" \u043d\u0435 \u043f\u0440\u043e\u0448\u0435\u043b \u0443\u0441\u043f\u0435\u0448\u043d\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438 \u043e\u0448\u0438\u0431\u043e\u043a \u043c\u043e\u0433\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0434\u0435\u0438\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 "Proxy Ticket" \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c "Service Ticket validator" \u0438\u043b\u0438 \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0441 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0435\u043c "renew request: true". -INVALID_REQUEST=\u041e\u0431\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 'service' \u0438 'ticket' \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b. -INVALID_TICKET="Ticket" ''{0}'' \u043d\u0435 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d. -INVALID_SERVICE="Ticket" ''{0}'' \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443. \u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0431\u044b\u043b ''{1}'' , \u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0431\u044b\u043b ''{2}'' -INVALID_PROXY_CALLBACK=\u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 proxy callback url ''{0}'' \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e\u0439\u0442\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. -UNAUTHORIZED_SERVICE_PROXY=\u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 ''{0}'' \u043d\u0435 \u0443\u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0435\u043d \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c CAS proxy authentication. - -screen.service.error.header=\u041f\u0440\u0435\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0435 \u0443\u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0435\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c CAS -screen.service.error.message=\u041f\u0440\u0435\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u044b\u0442\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0439\u0442\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438, \u043d\u0435 \u0443\u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0435\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c CAS. -screen.service.empty.error.message=\u0420\u0435\u0433\u0438\u0441\u0442\u0440 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043f\u0443\u0441\u0442. \u041f\u0440\u0435\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0435 CAS \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432\u0432\u0435\u0434\u0435\u043d\u044b \u0432 \u0440\u0435\u0433\u0438\u0441\u0442\u0440 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432. - -# Password policy -password.expiration.warning=\u0421\u0440\u043e\u043a \u0433\u043e\u0434\u043d\u043e\u0441\u0442\u0438 \u043f\u0430\u0440\u043e\u043b\u044f \u0438\u0441\u0442\u0435\u043a\u0430\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 {0} \u0434\u0435\u043d\u044c/\u0434\u043d\u0435\u0439. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u0441\u0432\u043e\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0441\u0435\u0439\u0447\u0430\u0441. -password.expiration.loginsRemaining=\u0423 \u0432\u0430\u0441 \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c {0} \u0432\u0445\u043e\u0434/\u0432\u0445\u043e\u0434\u043e\u0432 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u041e\u0411\u042f\u0417\u0410\u041d\u042b \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c. -screen.accountdisabled.heading=\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f. -screen.accountdisabled.message=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u0441\u0432\u044f\u0436\u0438\u0442\u0435\u0441\u044c \u0441 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u043c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u043c \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443. -screen.accountlocked.heading=\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0430. -screen.accountlocked.message=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u0441\u0432\u044f\u0436\u0438\u0442\u0435\u0441\u044c \u0441 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u043c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u043c \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443. -screen.expiredpass.heading=\u0421\u0440\u043e\u043a \u0433\u043e\u0434\u043d\u043e\u0441\u0442\u0438 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0430\u0440\u043e\u043b\u044f \u043f\u0440\u043e\u0441\u0440\u043e\u0447\u0435\u043d. -screen.expiredpass.message=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u043f\u043e\u043c\u0435\u043d\u044f\u0439\u0442\u0435 \u0432\u0430\u0448 \u043f\u0430\u0440\u043e\u043b\u044c. -screen.mustchangepass.heading=\u0412\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0432\u0430\u0448 \u043f\u0430\u0440\u043e\u043b\u044c. -screen.mustchangepass.message=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u043f\u043e\u043c\u0435\u043d\u044f\u0439\u0442\u0435 \u0432\u0430\u0448 \u043f\u0430\u0440\u043e\u043b\u044c. -screen.badhours.heading=\u0412\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u0437\u0430\u043f\u0440\u0435\u0449\u0451\u043d \u0432\u0445\u043e\u0434 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0432 \u0434\u0430\u043d\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u0443\u0442\u043e\u043a. -screen.badhours.message=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0432\u043e\u0439\u0442\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u043e\u0437\u0436\u0435. -screen.badworkstation.heading=\u0412\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u043e\u0439\u0442\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0441 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430. -screen.badworkstation.message=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u0441\u0432\u044f\u0436\u0438\u0442\u0435\u0441\u044c \u0441 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u043c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u043c \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443. - -# OAuth -screen.oauth.confirm.header=\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f -screen.oauth.confirm.message=\u0412\u044b \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u044b \u0434\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0432\u0430\u0448\u0435\u043c\u0443 \u043f\u0440\u043e\u0444\u0438\u043b\u044e \u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443: "{0}" ? -screen.oauth.confirm.allow=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f - -# Unavailable -screen.unavailable.heading=CAS \u043d\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d -screen.unavailable.message=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432\u0430\u0448\u0435\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430 \u0441\u0432\u044f\u0436\u0438\u0442\u0435\u0441\u044c \u0441 \u0432\u0430\u0448\u0435\u0439 \u0441\u043b\u0443\u0436\u0431\u043e\u0439 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0438\u043b\u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437. diff --git a/cas-server-webapp/src/main/resources/messages_sl.properties b/cas-server-webapp/src/main/resources/messages_sl.properties deleted file mode 100644 index 8880510016..0000000000 --- a/cas-server-webapp/src/main/resources/messages_sl.properties +++ /dev/null @@ -1,63 +0,0 @@ -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Dobrodo\u0161li v ARNES CAS online\! Uporabite uporabni\u0161ko ime in geslo, ki vam ga je dodeli administrator ARNES organizacije -screen.welcome.security=Zaradi varnostnih razlogov, prosimo, da naredite odjavo in zaprete brskalnik, ko zapustite spletni vir, ki je zahteval va\u0161o avtentikacijo. -screen.welcome.instructions=Vpi\u0161ite va\u0161o uporabni\u0161ko ime(eduprincipalName\: ime@arnes.si) in geslo. -screen.welcome.label.netid=eduPersonPrincipalName\: -screen.welcome.label.netid.accesskey=n -screen.welcome.label.password=Geslo\: -screen.welcome.label.password.accesskey=p -screen.welcome.label.warn=Opozori me, ko naredim novo prijavo v drugi spletni vir. -screen.welcome.label.warn.accesskey=w -screen.welcome.button.login=Prijava -screen.welcome.button.clear=ZBRI\u0160I - -#Confirmation Screen Messages -screen.confirmation.message=Klikni tukaj za vstop v aplikacijo. - -#Generic Success Screen Messages -screen.success.header=Prijava uspela -screen.success.success=Uspe\u0161no ste se prijavili v Centralno Avtenikacijsko Storitev. -screen.success.security=Zaradi varnostnih razlogov, prosimo, da naredite odjavo in zaprete brskalnik, ko zapustite spletni vir, ki je zahteval va\u0161o avtentikacijo. - -#Logout Screen Messages -screen.logout.header=Odjava uspela -screen.logout.success=Uspe\u0161no ste se prijavili v Centralno Avtenikacijsko Storitev. -screen.logout.security=Zaradi varnostnih razlogov zaprite brskalnik -screen.logout.redirect=Spletna storitev iz katere ste se odjavili, je priskrbela povezavo za nazaj, \u010De se \u017Eelite vrniti, kliknite na povezavo.. - -#Service Error Messages -screen.service.error.header=Ne avtorizerana Storitev -screen.service.error.message=Vstopiti ste hoteli do o spletne storitve nima dovoljenja do uporabe CAS storitve. - - -error.invalid.loginticket=Ne morete narediti re-submit forme, ki je \u017Ee bila poslana. -required.username=Uporabni\u0161ko ime je nujno vpisati\! -required.password=Geslo je nujno vpisati\! -error.authentication.credentials.bad=Veredostojnost, ki ste jo vpisali ne moremo dolo\u010Diti, da je pristno\! -error.authentication.credentials.unsupported=Veredostojnost, ki ste jo vpisali ni podprto v CAS-u\! - -INVALID_REQUEST_PROXY='pgt' in 'targetService' parametra sta oba nujna\! -INVALID_TICKET_SPEC=Ne uspe\u0161na validacija zahtevka. Mo\u017Ene napake so nastale pri vklju\u010Ditvi validacije v Proxy Ticket preko Service Ticket validacije. -INVALID_REQUEST='service' in 'ticket' parametra sta oba nujna\! -INVALID_TICKET=zahtevek ''{0}'' ni prepoznana -INVALID_SERVICE=zahtevek ''{0}'' se ne ujema priskrbljeno storitvijo diff --git a/cas-server-webapp/src/main/resources/messages_sv.properties b/cas-server-webapp/src/main/resources/messages_sv.properties deleted file mode 100644 index 2a9c208cf0..0000000000 --- a/cas-server-webapp/src/main/resources/messages_sv.properties +++ /dev/null @@ -1,68 +0,0 @@ -#Author: Fredrik Nilsson http://www.infoflexconnect.se -#Updated 2006-08-29: Pål Axelsson & Veronika Berglund IT Support Department at Uppsala University http://www.uu.se -#Updated 2007-06-21: Pål Axelsson IT Support Department at Uppsala University http://www.uu.se - -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Välkommen till den centrala autentiseringstjänsten CAS. När du installerat men ännu inte konfigurerat CAS kan du autentisera genom att ange samma text som både användaridentitet och lösenord för att prova CAS. -screen.welcome.security=Av säkerhetsskäl bör du logga ut och stänga webbläsaren när du är färdig med webbtjänsterna som kräver inloggning. -screen.welcome.instructions=Ange din användaridentitet och ditt lösenord. -screen.welcome.label.netid=Användarid: -screen.welcome.label.netid.accesskey=a -screen.welcome.label.password=Lösenord: -screen.welcome.label.password.accesskey=l -screen.welcome.label.warn=Varna mig innan jag loggar på en annan webbtjänst. -screen.welcome.label.warn.accesskey=v -screen.welcome.button.login=LOGGA IN -screen.welcome.button.clear=RENSA - -#Confirmation Screen Messages -screen.confirmation.message=Klicka här för att komma till webbtjänsten. - -#Generic Success Screen Messages -screen.success.header=Inloggningen lyckades -screen.success.success=Du har loggat in i den centrala autentiseringstjänsten CAS. -screen.success.security=Av säkerhetsskäl bör du logga ut och stänga webbläsaren när du är färdig med webbtjänsterna som kräver inloggning. - -#Logout Screen Messages -screen.logout.header=Du har loggat ut! -screen.logout.success=Du har loggat ut från den centrala autentiseringstjänsten CAS. -screen.logout.security=Av säkerhetsskäl bör du stänga din webbläsare. -screen.logout.redirect=Du kan logga in igen genom att klicka här. - -screen.service.sso.error.header=Du måste logga in igen för att använda denna webbtjänst -screen.service.sso.error.message=Du försökte använda en webbtjänst som kräver att du loggar in igen för att använda den. Logga in igen! - -error.invalid.loginticket=Du kan inte återanvända ett webbformulär som redan har skickats in. -required.username=Användaridentitet är en obligatoriskt uppgift. -required.password=Lösenord är en obligatoriskt uppgift. -error.authentication.credentials.bad=Inloggningsuppgifterna du angav kunde inte valideras! -error.authentication.credentials.unsupported=Inloggningsuppgifterna du angav kan inte hanteras av CAS. - -INVALID_REQUEST_PROXY=Både 'pgt' och 'targetService' är obligatoriska parametrar. -INVALID_TICKET_SPEC=Ticket-valideringen misslyckades. Möjliga fel skulle kunna vara att försöka validera en Proxy Ticket via en validator för Service Ticket, eller att en ny inloggning inte genomfördes trots begäran. -INVALID_REQUEST=Både 'service' och 'ticket' är obligatoriska parametrar. -INVALID_TICKET=ticket ''{0}'' känns inte igen. -INVALID_SERVICE=ticket ''{0}'' överenstämmer inte med angiven webbtjänst. - -screen.service.error.header=Ej auktoriserad webbtjänst -screen.service.error.message=Webbtjänsten du försökter ansluta till är ej auktoriserad att använda den centrala autentiseringstjänsten CAS. diff --git a/cas-server-webapp/src/main/resources/messages_tr.properties b/cas-server-webapp/src/main/resources/messages_tr.properties deleted file mode 100644 index 67381ffcba..0000000000 --- a/cas-server-webapp/src/main/resources/messages_tr.properties +++ /dev/null @@ -1,67 +0,0 @@ -# Author : Mert Caliskan -# http://www.jroller.com/mert - -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=Tebrikler!, CAS'\u0131 \u00e7al\u0131\u015f\u0131r hale getirdiniz. Haz\u0131rdaki kimliklendirme mekanizmas\u0131 kullan\u0131c\u0131 ad\u0131 ve parola ayn\u0131 oldu\u011fu durumlarda giri\u015fe izin vermektedir. Hemen deneyebilirsiniz. -screen.welcome.security=G\u00fcvenli\u011finiz i\u00e7in, i\u015finiz bittikten sonra kulland\u0131\u011f\u0131n\u0131z uygulamalardan \u00e7\u0131k\u0131\u015f yap\u0131n\u0131z ve taray\u0131c\u0131n\u0131z\u0131 kapat\u0131n\u0131z. -screen.welcome.instructions=Kullan\u0131c\u0131 ad\u0131 ve parolan\u0131z\u0131 giriniz -screen.welcome.label.netid=Kullan\u0131c\u0131 Ad\u0131: -screen.welcome.label.netid.accesskey=k -screen.welcome.label.password=Parola: -screen.welcome.label.password.accesskey=p -screen.welcome.label.warn=Di\u011fer sitelere girmeden \u00f6nce beni uyar. -screen.welcome.label.warn.accesskey=u -screen.welcome.button.login=G\u0130R\u0130\u015e -screen.welcome.button.clear=TEM\u0130ZLE - -#Confirmation Screen Messages -screen.confirmation.message=Uygulamaya eri\u015fmek i\u00e7in buraya t\u0131klay\u0131n\u0131z. - -#Generic Success Screen Messages -screen.success.header=Oturum ba\u015far\u0131yla a\u00e7\u0131ld\u0131. -screen.success.success=Merkezi Kimliklendirme Servisi'ne ba\u015far\u0131l\u0131 bir \u015fekilde giri\u015f yapt\u0131n\u0131z. -screen.success.security=G\u00fcvenlik nedenlerinden dolay\u0131, uygulamalar\u0131n kullan\u0131m\u0131 bittikten sonra sistemden \u00e7\u0131k\u0131\u015f yap\u0131p, taray\u0131c\u0131n\u0131z\u0131 kapat\u0131n\u0131z. - -#Logout Screen Messages -screen.logout.header=Oturum ba\u015far\u0131yla kapat\u0131ld\u0131. -screen.logout.success=Merkezi Kimliklendirme Servisi'nden ba\u015far\u0131l\u0131 bir \u015fekilde \u00e7\u0131k\u0131\u015f yapt\u0131n\u0131z. -screen.logout.security=G\u00fcvenlik nedenlerinden dolay\u0131, taray\u0131c\u0131n\u0131z\u0131 kapan\u0131t\u0131z. -screen.logout.redirect=Kimliklendirme servisi'ne y\u00f6nlendirme i\u00e7in verilen ba\u011flant\u0131ya t\u0131klayarak devam edebilirsiniz. - -screen.service.sso.error.header=Bu servise eri\u015fim i\u00e7in tekrar kimliklendirme gerekmektedir. -screen.service.sso.error.message=Bir servise ard\u0131\u015f\u0131k kimlik onay\u0131 yaparak eri\u015fmeye \u00e7al\u0131\u015ft\u0131n\u0131z. Onay i\u00e7in l\u00fctfen tekrar t\u0131klay\u0131n\u0131z. - -error.invalid.loginticket=\u00d6nceden g\u00f6nderilmi\u015f bir giri\u015f formunu tekrar g\u00f6nderemezsiniz. -required.username=Kullan\u0131c\u0131 Ad\u0131 girilmesi gerekli bir aland\u0131r. -required.password=Parola girilmesi gerekli bir aland\u0131r. -error.authentication.credentials.bad=Kullan\u0131c\u0131 Kodu veya Parola bilginizde yanl\u0131\u015fl\u0131k var. L\u00fctfen kontrol edip tekrar deneyiniz. -error.authentication.credentials.unsupported=Sa\u011flad\u0131\u011f\u0131n\u0131z kimliklendirme bilgileri Merkezi Kimliklendirme Sistemi taraf\u0131ndan tan\u0131nmamaktad\u0131r. - -INVALID_REQUEST_PROXY='pgt' ve 'targetService' parametrelerinin her ikisi birden gereklidir. -INVALID_TICKET_SPEC=Bilet do\u011frulama ba\u015far\u0131s\u0131z oldu. Olas\u0131 hatalar, servis bilet do\u011frulay\u0131c\u0131 ile Vekil (Proxy) bilet do\u011frulamak veya do\u011fru yenileme iste\u011fi kural\u0131na uyulmamas\u0131 olabilir. -INVALID_REQUEST='service' ve 'ticket' parametrelerinin her ikisi birden gereklidir. -INVALID_TICKET=Tan\u0131ms\u0131z bilet: ''{0}'' -INVALID_SERVICE=Bilet ''{0}'' belirtilen servis ile e\u015fle\u015fmiyor. As\u0131l servis: ''{1}'', belirtilen servis: ''{2}''. - -screen.service.error.header=Uygulama, Merkezi Kimliklendirme Servisi'ni kullanmak i\u00e7in yetkilendirilmemi\u015f. -screen.service.error.message=Kimliklendirme onay\u0131 yap\u0131lmaya \u00e7al\u0131\u015f\u0131lan uygulama, Merkezi Kimliklendirme Servisi'ni kullanmak i\u00e7in yetkilendirilmemi\u015f. diff --git a/cas-server-webapp/src/main/resources/messages_ur.properties b/cas-server-webapp/src/main/resources/messages_ur.properties deleted file mode 100644 index 295799e1d6..0000000000 --- a/cas-server-webapp/src/main/resources/messages_ur.properties +++ /dev/null @@ -1,66 +0,0 @@ -#Author: Faizan Ahmed (Rutgers University) -#Since 3.0.5 - -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=CAS ko online lany par Mubark baad! Default Tasdeek karney wala aap ki tasdeek iss soorat main karay ga agar password wo hi hoo jo user name hay. Aiye, aur try ki jiyay. -screen.welcome.security=Security ki wajoohat ki bina par aap mehrbani farma kar apnay web browser say Log Out aur Exit zaroor ki jiyay jub aap aisi services isstamal kar chookay hoon jo tasdeek chahti hoon. -screen.welcome.instructions=Apni Jasig ki NetID aur Password enter ki jiyay. -screen.welcome.label.netid=NetID: -screen.welcome.label.netid.accesskey=n -screen.welcome.label.password=Password: -screen.welcome.label.password.accesskey=p -screen.welcome.label.warn=Mujay doosri sites main login karnay say pahlay Khabardar karain. -screen.welcome.label.warn.accesskey=k -screen.welcome.button.login=LOGIN -screen.welcome.button.clear=CLEAR - -#Confirmation Screen Messages -screen.confirmation.message=Yahan Click karain agar app application main dakhil hona chahtay hain. - -#Generic Success Screen Messages -screen.success.header=Log In Kamyab -screen.success.success=Aap kamyabi say Centeral Authentication Service main login hoo chokay hain. -screen.success.security=Security ki wajoohat ki bina par jub aap aisi services isstamal kar chookay hoon jo tasdeek chahti hoon tou baraye mehrbani apnay web browser say Log Out aur Exit zaroor ki jiyay - -#Logout Screen Messages -screen.logout.header=Logout Kamyab -screen.logout.success=Aap kamyabi say Centeral Authentication Service say logout hoo chokay hain. -screen.logout.security=Security ki wajoohat ki bina par apnay web browser say exit karain. -screen.logout.redirect=Aap jis service say aye hain oos nay aik link supply kia hay jissay aap agar chahain tou follow kar saktay hain. - - -#Service Error Messages -screen.service.error.header=Bay Sanud Service -screen.service.error.message=Aap jiss service kay liay tasdeek ki kooshush kar rahay thay woo service CAS istamal karnay ki mijaz nahi. - -error.invalid.loginticket=Aap oos form ko dobara arsaal karnay ki kooshsish nahi kar saktay joo aap pahly arsal kar chookay hoon. -required.username=Username ka khana por karna lazmi hay. -required.password=Password ka khana por karna lazmi hay. -error.authentication.credentials.bad=Aap ka mohya kia howa waseeka (parteet puter) ki tasdeek karna momkin nahi. -error.authentication.credentials.unsupported=Aap kay mohya kiay howay waseeka (parteet puter) ko CAS support nahi karta. - -INVALID_REQUEST_PROXY='pgt' aur 'targetService' parameters doonon lazmi hain. -INVALID_TICKET_SPEC=Ticket toseek ki tasreeh par poora nahi utri. Momkin gultiyoon main shamil, hoo sakta hay kay proxy ticket ki toseek ki kooshish Service ticket kay toseek kaninda say ki gai hoo, yaa 'renew true request' say iss ki mitabkat na hooti hoo. -INVALID_REQUEST='service' aur 'ticket' parameters doonon lazmi hain. -INVALID_TICKET=ticket ''{0}'' ki shnakhat nahi hoo saki. -INVALID_SERVICE=ticket ''{0}'' ki mitabkat mohya karda service say nahi hoo saki. diff --git a/cas-server-webapp/src/main/resources/messages_zh_CN.properties b/cas-server-webapp/src/main/resources/messages_zh_CN.properties deleted file mode 100644 index e32f88b6f6..0000000000 --- a/cas-server-webapp/src/main/resources/messages_zh_CN.properties +++ /dev/null @@ -1,68 +0,0 @@ -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=\u6b22\u8fce\u6765\u5230\u4e2d\u592e\u8ba4\u8bc1\u7cfb\u7edf\u3002\u9ed8\u8ba4\u7684\u8ba4\u8bc1\u5904\u7406\u5668\u652f\u6301\u90a3\u4e9b\u7528\u6237\u540d\u7b49\u4e8e\u5bc6\u7801\u7684\u8d26\u53f7\uff0c\u5f00\u53d1\u8005\u53ef\u4ee5\u8bd5\u8bd5\u770b\u3002 -screen.welcome.security=\u51fa\u4e8e\u5b89\u5168\u8003\u8651\uff0c\u4e00\u65e6\u60a8\u8bbf\u95ee\u8fc7\u90a3\u4e9b\u9700\u8981\u60a8\u63d0\u4f9b\u51ed\u8bc1\u4fe1\u606f\u7684\u5e94\u7528\u65f6\uff0c\u8bf7\u64cd\u4f5c\u5b8c\u6210\u4e4b\u540e\u5173\u95ed\u6d4f\u89c8\u5668\u3002 -screen.welcome.instructions=\u8bf7\u8f93\u5165\u60a8\u7684\u7528\u6237\u540d\u548c\u5bc6\u7801. -screen.welcome.label.netid=\u7528\u6237\u540d: -screen.welcome.label.netid.accesskey=n -screen.welcome.label.password=\u5bc6\u3000\u7801: -screen.welcome.label.password.accesskey=p -screen.welcome.label.warn=\u8f6c\u5411\u5176\u4ed6\u7ad9\u70b9\u524d\u63d0\u793a\u6211\u3002 -screen.welcome.label.warn.accesskey=w -screen.welcome.button.login=\u767b\u5f55 -screen.welcome.button.clear=\u91cd\u7f6e - -# Blocked Errors Page -screen.blocked.header=\u8bbf\u95ee\u88ab\u62d2\u7edd -screen.blocked.message=\u8f93\u9519\u5bc6\u7801\u6b21\u6570\u592a\u591a\uff0c\u8d26\u53f7\u88ab\u9501\u5b9a\u3002 - -#Confirmation Screen Messages -screen.confirmation.message=\u5355\u51fb \u8fd9\u91cc \uff0c\u4fbf\u80fd\u591f\u8bbf\u95ee\u5230\u76ee\u6807\u5e94\u7528\u3002 - -#Generic Success Screen Messages -screen.success.header=\u767b\u5f55\u6210\u529f -screen.success.success=\u60a8\u5df2\u7ecf\u6210\u529f\u767b\u5f55\u4e2d\u592e\u8ba4\u8bc1\u7cfb\u7edf\u3002 -screen.success.security=\u51fa\u4e8e\u5b89\u5168\u8003\u8651\uff0c\u4e00\u65e6\u60a8\u8bbf\u95ee\u8fc7\u90a3\u4e9b\u9700\u8981\u60a8\u63d0\u4f9b\u51ed\u8bc1\u4fe1\u606f\u7684\u5e94\u7528\u65f6\uff0c\u8bf7\u64cd\u4f5c\u5b8c\u6210\u4e4b\u540e\u5173\u95ed\u6d4f\u89c8\u5668\u3002 - -#Logout Screen Messages -screen.logout.header=\u6ce8\u9500\u6210\u529f -screen.logout.success=\u60a8\u5df2\u7ecf\u6210\u529f\u9000\u51faCAS\u7cfb\u7edf\uff0c\u8c22\u8c22\u4f7f\u7528\uff01 -screen.logout.security=\u51fa\u4e8e\u5b89\u5168\u8003\u8651\uff0c\u8bf7\u5173\u95ed\u60a8\u7684\u6d4f\u89c8\u5668\u3002 -screen.logout.redirect=\u60a8\u53ef\u4ee5\u901a\u8fc7\u5982\u4e0bURL\u8bbf\u95ee\u5230\u76ee\u6807\u670d\u52a1\uff1a\u76ee\u6807\u670d\u52a1. - -screen.service.sso.error.header=\u5728\u8bbf\u95ee\u5230\u5230\u76ee\u6807\u670d\u52a1\u524d\uff0c\u4f60\u5fc5\u987b\u7ecf\u8fc7\u91cd\u65b0\u8ba4\u8bc1\u7684\u8003\u9a8c -screen.service.sso.error.message=\u4f60\u6b63\u8bd5\u56fe\u8bbf\u95ee\u8981\u6c42\u91cd\u65b0\u8ba4\u8bc1\u7684\u670d\u52a1\u3002\u8bf7\u5c1d\u8bd5\u8fdb\u884c\u518d\u6b21\u8ba4\u8bc1\u3002 - -error.invalid.loginticket=\u60a8\u4e0d\u80fd\u591f\u518d\u6b21\u63d0\u4ea4\u5df2\u7ecf\u63d0\u4ea4\u8fc7\u7684\u8868\u5355\u3002 -required.username=\u5fc5\u987b\u5f55\u5165\u7528\u6237\u540d\u3002 -required.password=\u5fc5\u987b\u5f55\u5165\u5bc6\u7801\u3002 -error.authentication.credentials.bad=\u60a8\u63d0\u4f9b\u7684\u51ed\u8bc1\u6709\u8bef\u3002 -error.authentication.credentials.unsupported=CAS\u4e0d\u652f\u6301\u60a8\u63d0\u4f9b\u7684\u51ed\u8bc1\u3002 - -INVALID_REQUEST_PROXY=\u5fc5\u987b\u540c\u65f6\u63d0\u4f9b'pgt'\u548c'targetService'\u53c2\u6570 -INVALID_TICKET_SPEC=\u6821\u9a8c\u7968\u6839\u5931\u8d25\u3002\u60a8\u53ef\u80fd\u91c7\u7528\u670d\u52a1\u7968\u6839\u6765\u6821\u9a8c\u4ee3\u7406\u7968\u6839\uff0c\u6216\u6ca1\u6709\u5c06renew\u8bbe\u4e3atrue\u3002 -INVALID_REQUEST=\u5fc5\u987b\u540c\u65f6\u63d0\u4f9b'service'\u548c'ticket'\u53c2\u6570 -INVALID_TICKET=\u672a\u80fd\u591f\u8bc6\u522b\u51fa\u76ee\u6807 ''{0}''\u7968\u6839 -INVALID_SERVICE=\u7968\u6839''{0}''\u4e0d\u7b26\u5408\u76ee\u6807\u670d\u52a1 - -screen.service.error.header=\u672a\u8ba4\u8bc1\u6388\u6743\u7684\u670d\u52a1 -screen.service.error.message=\u4e0d\u5141\u8bb8\u4f7f\u7528CAS\u6765\u8ba4\u8bc1\u60a8\u8bbf\u95ee\u7684\u76ee\u6807\u5e94\u7528\u3002 diff --git a/cas-server-webapp/src/main/resources/messages_zh_TW.properties b/cas-server-webapp/src/main/resources/messages_zh_TW.properties deleted file mode 100644 index 33fd0b23f8..0000000000 --- a/cas-server-webapp/src/main/resources/messages_zh_TW.properties +++ /dev/null @@ -1,68 +0,0 @@ -#Welcome Screen Messages - -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -screen.welcome.welcome=\u6b61\u8fce\u4f86\u5230\u4e2d\u592e\u8a8d\u8b49\u7cfb\u7d71\u3002\u9ed8\u8a8d\u7684\u8a8d\u8b49\u8655\u7406\u5668\u652f\u6301\u90a3\u4e9b\u7528\u6236\u540d\u7b49\u65bc\u5bc6\u78bc\u7684\u8cec\u865f\uff0c\u958b\u767c\u8005\u53ef\u4ee5\u8a66\u8a66\u770b\u3002 -screen.welcome.security=\u51fa\u65bc\u5b89\u5168\u8003\u616e\uff0c\u4e00\u65e6\u60a8\u8a2a\u554f\u904e\u90a3\u4e9b\u9700\u8981\u60a8\u63d0\u4f9b\u6191\u8b49\u4fe1\u606f\u7684\u61c9\u7528\u6642\uff0c\u8acb\u64cd\u4f5c\u5b8c\u6210\u4e4b\u5f8c\u95dc\u9589\u700f\u89bd\u5668\u3002 -screen.welcome.instructions=\u8acb\u8f38\u5165\u60a8\u7684\u7528\u6236\u540d\u548c\u5bc6\u78bc. -screen.welcome.label.netid=\u7528\u6236\u540d: -screen.welcome.label.netid.accesskey=n -screen.welcome.label.password=\u5bc6\u3000\u78bc: -screen.welcome.label.password.accesskey=p -screen.welcome.label.warn=\u8f49\u5411\u5176\u4ed6\u7ad9\u9ede\u524d\u63d0\u793a\u6211\u3002 -screen.welcome.label.warn.accesskey=w -screen.welcome.button.login=\u767b\u9304 -screen.welcome.button.clear=\u91cd\u7f6e - -# Blocked Errors Page -screen.blocked.header=\u8a2a\u554f\u88ab\u62d2\u7d55 -screen.blocked.message=\u8f38\u932f\u5bc6\u78bc\u6b21\u6578\u592a\u591a\uff0c\u8cec\u865f\u88ab\u9396\u5b9a\u3002 - -#Confirmation Screen Messages -screen.confirmation.message=\u55ae\u64ca\u9019\u88e1 \uff0c\u4fbf\u80fd\u5920\u8a2a\u554f\u5230\u76ee\u6a19\u61c9\u7528\u3002 - -#Generic Success Screen Messages -screen.success.header=\u767b\u9304\u6210\u529f -screen.success.success=\u60a8\u5df2\u7d93\u6210\u529f\u767b\u9304\u4e2d\u592e\u8a8d\u8b49\u7cfb\u7d71\u3002 -screen.success.security=\u51fa\u65bc\u5b89\u5168\u8003\u616e\uff0c\u4e00\u65e6\u60a8\u8a2a\u554f\u904e\u90a3\u4e9b\u9700\u8981\u60a8\u63d0\u4f9b\u6191\u8b49\u4fe1\u606f\u7684\u61c9\u7528\u6642\uff0c\u8acb\u64cd\u4f5c\u5b8c\u6210\u4e4b\u5f8c\u95dc\u9589\u700f\u89bd\u5668\u3002 - -#Logout Screen Messages -screen.logout.header=\u8a3b\u92b7\u6210\u529f -screen.logout.success=\u60a8\u5df2\u7d93\u6210\u529f\u9000\u51faCAS\u7cfb\u7d71\uff0c\u8b1d\u8b1d\u4f7f\u7528\uff01 -screen.logout.security=\u51fa\u65bc\u5b89\u5168\u8003\u616e\uff0c\u8acb\u95dc\u9589\u60a8\u7684\u700f\u89bd\u5668\u3002 -screen.logout.redirect=\u60a8\u53ef\u4ee5\u901a\u904e\u5982\u4e0bURL\u8a2a\u554f\u5230\u76ee\u6a19\u670d\u52d9\uff1a\u76ee\u6a19\u670d\u52d9. - -screen.service.sso.error.header=\u5728\u8a2a\u554f\u5230\u5230\u76ee\u6a19\u670d\u52d9\u524d\uff0c\u4f60\u5fc5\u9808\u7d93\u904e\u91cd\u65b0\u8a8d\u8b49\u7684\u8003\u9a57 -screen.service.sso.error.message=\u4f60\u6b63\u8a66\u5716\u8a2a\u554f\u8981\u6c42\u91cd\u65b0\u8a8d\u8b49\u7684\u670d\u52d9\u3002\u8acb\u5617\u8a66\u9032\u884c\u518d\u6b21\u8a8d\u8b49\u3002 - -error.invalid.loginticket=\u60a8\u4e0d\u80fd\u5920\u518d\u6b21\u63d0\u4ea4\u5df2\u7d93\u63d0\u4ea4\u904e\u7684\u8868\u55ae\u3002 -required.username=\u5fc5\u9808\u9304\u5165\u7528\u6236\u540d\u3002 -required.password=\u5fc5\u9808\u9304\u5165\u5bc6\u78bc\u3002 -error.authentication.credentials.bad=\u60a8\u63d0\u4f9b\u7684\u6191\u8b49\u6709\u8aa4\u3002 -error.authentication.credentials.unsupported=CAS\u4e0d\u652f\u6301\u60a8\u63d0\u4f9b\u7684\u6191\u8b49\u3002 - -INVALID_REQUEST_PROXY=\u5fc5\u9808\u540c\u6642\u63d0\u4f9b'pgt'\u548c'targetService'\u53c3\u6578 -INVALID_TICKET_SPEC=\u6821\u9a57\u7968\u6839\u5931\u6557\u3002\u60a8\u53ef\u80fd\u63a1\u7528\u670d\u52d9\u7968\u6839\u4f86\u6821\u9a57\u4ee3\u7406\u7968\u6839\uff0c\u6216\u6c92\u6709\u5c07renew\u8a2d\u70batrue\u3002 -INVALID_REQUEST=\u5fc5\u9808\u540c\u6642\u63d0\u4f9b'service'\u548c'ticket'\u53c3\u6578 -INVALID_TICKET=\u672a\u80fd\u5920\u8b58\u5225\u51fa\u76ee\u6a19''{0}''\u7968\u6839 -INVALID_SERVICE=\u7968\u6839''{0}''\u4e0d\u7b26\u5408\u76ee\u6a19\u670d\u52d9 - -screen.service.error.header=\u672a\u8a8d\u8b49\u6388\u6b0a\u7684\u670d\u52d9 -screen.service.error.message=\u4e0d\u5141\u8a31\u4f7f\u7528CAS\u4f86\u8a8d\u8b49\u60a8\u8a2a\u554f\u7684\u76ee\u6a19\u61c9\u7528\u3002 diff --git a/cas-server-webapp/src/main/resources/protocol_views.properties b/cas-server-webapp/src/main/resources/protocol_views.properties deleted file mode 100644 index 5c66513e0e..0000000000 --- a/cas-server-webapp/src/main/resources/protocol_views.properties +++ /dev/null @@ -1,72 +0,0 @@ -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -### 1.0 validation responses (/validate) -cas1ServiceFailureView.(class)=org.jasig.cas.web.view.Cas10ResponseView -cas1ServiceFailureView.successResponse=false - -cas1ServiceSuccessView.(class)=org.jasig.cas.web.view.Cas10ResponseView -cas1ServiceSuccessView.successResponse=true - -### CAS 2.0 Response Protocol Views - -## Validation Response Protocol Views: /proxyValidate, /serviceValidate -cas2ServiceSuccessView.(class)=org.springframework.web.servlet.view.JstlView -cas2ServiceSuccessView.url=/WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp - -cas2ServiceFailureView.(class)=org.springframework.web.servlet.view.JstlView -cas2ServiceFailureView.url=/WEB-INF/view/jsp/protocol/2.0/casServiceValidationFailure.jsp - -## Proxy Response Protocol Views: /proxy -cas2ProxyFailureView.(class)=org.springframework.web.servlet.view.JstlView -cas2ProxyFailureView.url=/WEB-INF/view/jsp/protocol/2.0/casProxyFailureView.jsp - -cas2ProxySuccessView.(class)=org.springframework.web.servlet.view.JstlView -cas2ProxySuccessView.url=/WEB-INF/view/jsp/protocol/2.0/casProxySuccessView.jsp - -### CAS 3.0 Response Protocol Views: /proxyValidate, /serviceValidate -cas3ServiceSuccessView.(class)=org.springframework.web.servlet.view.JstlView -cas3ServiceSuccessView.url=/WEB-INF/view/jsp/protocol/3.0/casServiceValidationSuccess.jsp - -cas3ServiceFailureView.(class)=org.springframework.web.servlet.view.JstlView -cas3ServiceFailureView.url=/WEB-INF/view/jsp/protocol/3.0/casServiceValidationFailure.jsp - -#OpenId Views -casOpenIdServiceFailureView.(class)=org.springframework.web.servlet.view.JstlView -casOpenIdServiceFailureView.url=/WEB-INF/view/jsp/protocol/openid/casOpenIdServiceFailureView.jsp - -casOpenIdServiceSuccessView.(class)=org.springframework.web.servlet.view.JstlView -casOpenIdServiceSuccessView.url=/WEB-INF/view/jsp/protocol/openid/casOpenIdServiceSuccessView.jsp - -casOpenIdAssociationFailureView.(class)=org.springframework.web.servlet.view.JstlView -casOpenIdAssociationFailureView.url=/WEB-INF/view/jsp/protocol/openid/casOpenIdAssociationFailureView.jsp - -casOpenIdAssociationSuccessView.(class)=org.springframework.web.servlet.view.JstlView -casOpenIdAssociationSuccessView.url=/WEB-INF/view/jsp/protocol/openid/casOpenIdAssociationSuccessView.jsp - -openIdProviderView.(class)=org.springframework.web.servlet.view.JstlView -openIdProviderView.url=/WEB-INF/view/jsp/protocol/openid/user.jsp - -### Post View -postResponseView.(class)=org.springframework.web.servlet.view.JstlView -postResponseView.url=/WEB-INF/view/jsp/protocol/casPostResponseView.jsp - -### OAuth View -oauthConfirmView.(class)=org.springframework.web.servlet.view.JstlView -oauthConfirmView.url=/WEB-INF/view/jsp/protocol/oauth/confirm.jsp diff --git a/cas-server-webapp/src/main/resources/saml_views.properties b/cas-server-webapp/src/main/resources/saml_views.properties deleted file mode 100644 index 2a776e4d1d..0000000000 --- a/cas-server-webapp/src/main/resources/saml_views.properties +++ /dev/null @@ -1,24 +0,0 @@ -# -# Licensed to Jasig under one or more contributor license -# agreements. See the NOTICE file distributed with this work -# for additional information regarding copyright ownership. -# Jasig licenses this file to you 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 the following location: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -### SAML Views -casSamlServiceSuccessView.(class)=org.jasig.cas.support.saml.web.view.Saml10SuccessResponseView -casSamlServiceSuccessView.issuer=localhost - -casSamlServiceFailureView.(class)=org.jasig.cas.support.saml.web.view.Saml10FailureResponseView diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/cas-servlet.xml b/cas-server-webapp/src/main/webapp/WEB-INF/cas-servlet.xml deleted file mode 100644 index 50eb29c646..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/cas-servlet.xml +++ /dev/null @@ -1,272 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - ${cas.viewResolver.basename} - protocol_views - - - - - - - - - - - - - - - - - - - serviceValidateController - proxyValidateController - - v3ServiceValidateController - v3ProxyValidateController - - legacyValidateController - proxyController - passThroughController - healthCheckController - statisticsController - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/deployerConfigContext.xml b/cas-server-webapp/src/main/webapp/WEB-INF/deployerConfigContext.xml deleted file mode 100644 index 25b56037c3..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/deployerConfigContext.xml +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/login-webflow.xml b/cas-server-webapp/src/main/webapp/WEB-INF/login-webflow.xml deleted file mode 100644 index b64adb971a..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/login-webflow.xml +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/logout-webflow.xml b/cas-server-webapp/src/main/webapp/WEB-INF/logout-webflow.xml deleted file mode 100644 index d7d1488cff..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/logout-webflow.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/restlet-servlet.xml b/cas-server-webapp/src/main/webapp/WEB-INF/restlet-servlet.xml deleted file mode 100644 index e6552ee7cb..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/restlet-servlet.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/README.txt b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/README.txt deleted file mode 100644 index e3f2cbffdb..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/README.txt +++ /dev/null @@ -1,26 +0,0 @@ -INTRODUCTION -The spring-configuration directory is a "convention-over-configuration" option -for CAS deployers. It allows you to drop a Spring XML configuration file into -this directory and have CAS automatically find it (after the typical application -restart). It eliminates the need for you to register that file in the web.xml - -ADVANTAGES -By automatically breaking the configuration into smaller "bite-sized" pieces -you can easily override small components of CAS without worrying about merging -huge pieces of configurations files together later. - -The configuration-over-convention option also allows you to add new configuration -options without editing existing configuration files. - -This should make tracking changes and maintaining local modifications easier. - -GOTCHAS AND THINGS TO WATCH OUT FOR -If you name a local bean and an existing bean the same thing, there will be a major -collision. Deployment will fail. The sky will fall! (okay that last part isn't -true). Spring will be merging all of these files together so every bean must -have unique names. The only way around this is if you override the file completely. -i.e. override the ticketRegistry.xml allows you to re-use the "ticketRegistry" -id. - -In addition, if there is a typographical/XML parsing error in a file, the -application will not deploy. diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/applicationContext.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/applicationContext.xml deleted file mode 100644 index 8ca58081db..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/applicationContext.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - This is the main Spring configuration file with some of the main "core" classes defined. You shouldn't really - modify this unless you - know what you're doing! - - - - - - - - - - - - - - classpath:custom_messages - classpath:messages - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/argumentExtractorsConfiguration.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/argumentExtractorsConfiguration.xml deleted file mode 100644 index aa322f3d19..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/argumentExtractorsConfiguration.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - Argument Extractors are what are used to translate HTTP requests into requests of the appropriate protocol (i.e. CAS, SAML, SAML2, - OpenId, etc.). By default, only CAS is enabled. - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/auditTrailContext.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/auditTrailContext.xml deleted file mode 100644 index e577f4751c..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/auditTrailContext.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - Configuration file for the Inspektr package which handles auditing for Java applications. - If enabled this should be modified to log audit and statistics information the same way - your local applications do. The default is currently to log to the console which is good - for debugging/testing purposes. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/filters.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/filters.xml deleted file mode 100644 index 2481a0da67..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/filters.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/log4jConfiguration.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/log4jConfiguration.xml deleted file mode 100644 index 6a72a5c3dd..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/log4jConfiguration.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - Log4J initialization. Configuration options are sourced from cas.properties. This allows deployers to externalize - both cas.properties and log4j.xml, so that a single cas.war file can be deployed to multiple tiers or hosts without - having to do any post configuration. This approach helps to preserve configuration between upgrades. - - Deployers should not have to edit this file. - - - - - - ${log4j.config.location:classpath:log4j.xml} - ${log4j.refresh.interval:60000} - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/propertyFileConfigurer.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/propertyFileConfigurer.xml deleted file mode 100644 index 5092c51ab8..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/propertyFileConfigurer.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - This file lets CAS know where you've stored the cas.properties file which details some of the configuration options - that are specific to your environment. You can specify the location of the file here. You may wish to place the file outside - of the Servlet context if you have options that are specific to a tier (i.e. test vs. production) so that the WAR file - can be moved between tiers without modification. - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/securityContext.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/securityContext.xml deleted file mode 100644 index 6b12a88892..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/securityContext.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - Security configuration for sensitive areas of CAS : status and statistics. - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketExpirationPolicies.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketExpirationPolicies.xml deleted file mode 100644 index 293ad054fd..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketExpirationPolicies.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - Assignment of expiration policies for the different tickets generated by CAS including ticket granting ticket - (TGT), service ticket (ST), proxy granting ticket (PGT), and proxy ticket (PT). - These expiration policies determine how long the ticket they are assigned to can be used and even how often they - can be used before becoming expired / invalid. - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml deleted file mode 100644 index 272ca975e8..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - Defines the cookie that stores the TicketGrantingTicket. You most likely should never modify these (especially the "secure" property). - You can change the name if you want to make it harder for people to guess. - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketRegistry.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketRegistry.xml deleted file mode 100644 index 684e3200ca..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketRegistry.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - Configuration for the default TicketRegistry which stores the tickets in-memory and cleans them out as specified intervals. - - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/uniqueIdGenerators.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/uniqueIdGenerators.xml deleted file mode 100644 index efebc0abf8..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/uniqueIdGenerators.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - Controls the generation of the unique identifiers for tickets. You most likely do not need to modify these. Though you may need to add - the SAML ticket id generator. - - - - - - - - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/warnCookieGenerator.xml b/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/warnCookieGenerator.xml deleted file mode 100644 index 3a7993dd29..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/warnCookieGenerator.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - This Spring Configuration file describes the cookie used to store the WARN parameter so that a user is warned whenever the CAS service - is used. You would modify this if you wanted to change the cookie path or the name. - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/authorizationFailure.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/authorizationFailure.jsp deleted file mode 100644 index 788e912a14..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/authorizationFailure.jsp +++ /dev/null @@ -1,60 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - - -<%@ page import="org.jasig.cas.web.support.WebUtils"%> -<%@ page import="org.springframework.security.web.WebAttributes"%> - - -
-

- <% - // Look for details of authorization failure in well-known request attributes. - final String[] keys = new String[] {WebUtils.CAS_ACCESS_DENIED_REASON, WebAttributes.AUTHENTICATION_EXCEPTION}; - Object detail = null; - for (String key : keys) { - detail = request.getAttribute(key); - if (detail == null) { - detail = request.getSession().getAttribute(key); - request.getSession().removeAttribute(key); - } - if (detail != null) { - break; - } - } - if (detail instanceof String) { - request.setAttribute("messageKey", detail); - } else if (detail instanceof Exception) { - final Exception cause = (Exception) detail; - final String message = String.format("%s::%s", cause.getClass().getSimpleName(), cause.getMessage()); - request.setAttribute("message", message); - } - %> - - -

-
- -

-
-
-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casAccountDisabledView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casAccountDisabledView.jsp deleted file mode 100644 index 6d53685f91..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casAccountDisabledView.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casAccountLockedView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casAccountLockedView.jsp deleted file mode 100644 index 57f2a23417..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casAccountLockedView.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casBadHoursView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casBadHoursView.jsp deleted file mode 100644 index 28e5b02351..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casBadHoursView.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casBadWorkstationView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casBadWorkstationView.jsp deleted file mode 100644 index b8e8e8727a..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casBadWorkstationView.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casConfirmView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casConfirmView.jsp deleted file mode 100644 index be8d0a6ecc..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casConfirmView.jsp +++ /dev/null @@ -1,25 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casExpiredPassView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casExpiredPassView.jsp deleted file mode 100644 index 310135a5fb..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casExpiredPassView.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casGenericSuccess.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casGenericSuccess.jsp deleted file mode 100644 index 6523c3ca05..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casGenericSuccess.jsp +++ /dev/null @@ -1,28 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-

-

-
- - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casLoginMessageView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casLoginMessageView.jsp deleted file mode 100644 index a18f107c20..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casLoginMessageView.jsp +++ /dev/null @@ -1,37 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - - -
-

Authentication Succeeded with Warnings

- - -

${message.text}

-
- -
- - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casLoginView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casLoginView.jsp deleted file mode 100644 index abedfeded9..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casLoginView.jsp +++ /dev/null @@ -1,152 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - - - -
-

Non-secure Connection

-

You are currently accessing CAS over a non-secure connection. Single Sign On WILL NOT WORK. In order to have single sign on work, you MUST log in over HTTPS.

-
-
- -
- - - - -

- -
- - - - ${sessionScope.openIdLocalId} - - - - - - - -
- -
- - <%-- - NOTE: Certain browsers will offer the option of caching passwords for a user. There is a non-standard attribute, - "autocomplete" that when set to "off" will tell certain browsers not to prompt to cache credentials. For more - information, see the following web page: - http://www.technofundo.com/tech/web/ie_autocomplete.html - --%> - - -
- -
- " type="checkbox" /> - -
- -
- - - - - " tabindex="4" type="submit" /> - " tabindex="5" type="reset" /> -
-
-
- - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casLogoutView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casLogoutView.jsp deleted file mode 100644 index 6faa8c1eb9..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casLogoutView.jsp +++ /dev/null @@ -1,27 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-

-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casMustChangePassView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casMustChangePassView.jsp deleted file mode 100644 index 8e9f5884a9..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/casMustChangePassView.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/includes/bottom.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/includes/bottom.jsp deleted file mode 100644 index 5f151c3184..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/includes/bottom.jsp +++ /dev/null @@ -1,47 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - - - - - - - - - - <%-- - JavaScript Debug: A simple wrapper for console.log - See this link for more info: http://benalman.com/projects/javascript-debug-console-log/ - - - --%> - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/includes/top.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/includes/top.jsp deleted file mode 100644 index 27b58c6eac..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/includes/top.jsp +++ /dev/null @@ -1,50 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - - -<%@ page pageEncoding="UTF-8" %> -<%@ page contentType="text/html; charset=UTF-8" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> - - - - - - CAS – Central Authentication Service - - - " /> - " type="image/x-icon" /> - - - - -
-
- -

Central Authentication Service (CAS)

-
-
diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/serviceErrorSsoView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/serviceErrorSsoView.jsp deleted file mode 100644 index 9ed3f92706..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/serviceErrorSsoView.jsp +++ /dev/null @@ -1,31 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - - - - - - -
-

-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/serviceErrorView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/serviceErrorView.jsp deleted file mode 100644 index 65e4d06083..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui/serviceErrorView.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/errors.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/errors.jsp deleted file mode 100644 index de26a99a8b..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/errors.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

-

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/monitoring/viewStatistics.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/monitoring/viewStatistics.jsp deleted file mode 100644 index 739f301219..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/monitoring/viewStatistics.jsp +++ /dev/null @@ -1,102 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@include file="/WEB-INF/view/jsp/default/ui/includes/top.jsp"%> - -

Runtime Statistics

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyValue
Server${serverIpAddress} (${serverHostName})
CAS Ticket Suffix${casTicketSuffix}
Server Start Time${startTime}
Uptime${upTime}
Memory ${freeMemory} MB free ${totalMemory} MB total
Maximum Memory${maxMemory} MB
Available Processors${availableProcessors}
- -

- -

Ticket Registry Statistics

- - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyValue
Unexpired TGTs${unexpiredTgts}
Unexpired STs${unexpiredSts}
Expired TGTs${expiredTgts}
Expired STs${expiredSts}
- -

Performance Statistics

- - -

${appender.name}

- -${appender.name} - -
- -<%@include file="/WEB-INF/view/jsp/default/ui/includes/bottom.jsp" %> diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casProxyFailureView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casProxyFailureView.jsp deleted file mode 100644 index 9c060735cf..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casProxyFailureView.jsp +++ /dev/null @@ -1,27 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page session="false" contentType="application/xml; charset=UTF-8" %> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> - - - ${fn:escapeXml(description)} - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casProxySuccessView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casProxySuccessView.jsp deleted file mode 100644 index bccf2ba6a9..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casProxySuccessView.jsp +++ /dev/null @@ -1,27 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page session="false" contentType="application/xml; charset=UTF-8" %> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> - - - ${fn:escapeXml(ticket)} - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casServiceValidationFailure.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casServiceValidationFailure.jsp deleted file mode 100644 index f4964b85df..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casServiceValidationFailure.jsp +++ /dev/null @@ -1,27 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page session="false" contentType="application/xml; charset=UTF-8" %> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> - - - ${fn:escapeXml(description)} - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp deleted file mode 100644 index 545ec13a93..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp +++ /dev/null @@ -1,38 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page session="false" contentType="application/xml; charset=UTF-8" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> - - - ${fn:escapeXml(assertion.primaryAuthentication.principal.id)} - - ${pgtIou} - - - - - ${fn:escapeXml(proxy.principal.id)} - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/3.0/casServiceValidationFailure.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/3.0/casServiceValidationFailure.jsp deleted file mode 100644 index f4964b85df..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/3.0/casServiceValidationFailure.jsp +++ /dev/null @@ -1,27 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page session="false" contentType="application/xml; charset=UTF-8" %> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> - - - ${fn:escapeXml(description)} - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/3.0/casServiceValidationSuccess.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/3.0/casServiceValidationSuccess.jsp deleted file mode 100644 index e07eaf3759..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/3.0/casServiceValidationSuccess.jsp +++ /dev/null @@ -1,70 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page session="false" contentType="application/xml; charset=UTF-8" %> -<%@ page import="java.util.Map.Entry" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> - - - ${fn:escapeXml(assertion.primaryAuthentication.principal.id)} - - ${pgtIou} - - - - - ${fn:escapeXml(proxy.principal.id)} - - - - - - - - <%-- ${attr.value['class'].simpleName} fails for List: use scriptlet instead --%> - <% - Entry entry = (Entry) pageContext.getAttribute("attr"); - Object value = entry.getValue(); - pageContext.setAttribute("isAString", value instanceof String); - %> - - <%-- it's a String, output it once --%> - - ${fn:escapeXml(attr.value)} - - <%-- if attribute is multi-valued, list each value under the same attribute name --%> - - - ${fn:escapeXml(attrval)} - - - - - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/casPostResponseView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/casPostResponseView.jsp deleted file mode 100644 index 35478f3007..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/casPostResponseView.jsp +++ /dev/null @@ -1,37 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page language="java" session="false"%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -
" method="post"> -
- - - -
- -
- - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/clearPass/clearPassFailure.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/clearPass/clearPassFailure.jsp deleted file mode 100644 index 7641f54ee8..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/clearPass/clearPassFailure.jsp +++ /dev/null @@ -1,25 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page session="false" contentType="application/xml; charset=UTF-8" %> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> - - ${fn:escapeXml(description)} - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/clearPass/clearPassSuccess.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/clearPass/clearPassSuccess.jsp deleted file mode 100644 index bd284997aa..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/clearPass/clearPassSuccess.jsp +++ /dev/null @@ -1,28 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page session="false" contentType="application/xml; charset=UTF-8" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> - - - ${fn:escapeXml(credentials)} - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/oauth/confirm.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/oauth/confirm.jsp deleted file mode 100644 index 508fba0b19..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/oauth/confirm.jsp +++ /dev/null @@ -1,32 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - -
-

- -

- -

-

- -

-
- diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdAssociationFailureView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdAssociationFailureView.jsp deleted file mode 100644 index 07f52270e2..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdAssociationFailureView.jsp +++ /dev/null @@ -1,21 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%= "openid.mode:cancel\n" %> diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdAssociationSuccessView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdAssociationSuccessView.jsp deleted file mode 100644 index 7ef2952d86..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdAssociationSuccessView.jsp +++ /dev/null @@ -1,30 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page import="java.util.Set, java.util.Map, java.util.Iterator" %> -<% - Map parameters = (Map)request.getAttribute("parameters"); - Iterator iterator = parameters.keySet().iterator(); - while (iterator.hasNext()) { - String key = (String)iterator.next(); - String parameter = (String)parameters.get(key); - out.print(key+":"+parameter+"\n"); - } -%> diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdServiceFailureView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdServiceFailureView.jsp deleted file mode 100644 index 009b6ff407..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdServiceFailureView.jsp +++ /dev/null @@ -1,21 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%= "openid.mode:id_res\nis_valid:false\n" %> diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdServiceSuccessView.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdServiceSuccessView.jsp deleted file mode 100644 index 0982bcc7db..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/casOpenIdServiceSuccessView.jsp +++ /dev/null @@ -1,21 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%= "openid.mode:id_res\nis_valid:true\n" %> diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/user.jsp b/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/user.jsp deleted file mode 100644 index a268556a1b..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/openid/user.jsp +++ /dev/null @@ -1,25 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> - - - - - diff --git a/cas-server-webapp/src/main/webapp/WEB-INF/web.xml b/cas-server-webapp/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index b3f2d3d14f..0000000000 --- a/cas-server-webapp/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,212 +0,0 @@ - - - - Central Authentication System (CAS) ${project.version} - - - - - contextConfigLocation - - /WEB-INF/spring-configuration/*.xml - /WEB-INF/deployerConfigContext.xml - - - - - CAS Client Info Logging Filter - com.github.inspektr.common.web.ClientInfoThreadLocalFilter - - - CAS Client Info Logging Filter - /* - - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - springSecurityFilterChain - /status - - - springSecurityFilterChain - /statistics - - - - characterEncodingFilter - org.springframework.web.filter.DelegatingFilterProxy - - - characterEncodingFilter - /* - - - - - - org.jasig.cas.web.init.SafeContextLoaderListener - - - - - - cas - - org.jasig.cas.web.init.SafeDispatcherServlet - - - publishContext - false - - 1 - - - - cas - /login - - - - cas - /logout - - - - cas - /validate - - - - cas - /serviceValidate - - - - cas - /p3/serviceValidate - - - - cas - /proxy - - - - cas - /proxyValidate - - - - cas - /p3/proxyValidate - - - - cas - /CentralAuthenticationService - - - - cas - /status - - - - cas - /statistics - - - - cas - /authorizationFailure.html - - - - - 5 - - - - 401 - /authorizationFailure.html - - - - 403 - /authorizationFailure.html - - - - 404 - / - - - - 500 - /WEB-INF/view/jsp/errors.jsp - - - - 501 - /WEB-INF/view/jsp/errors.jsp - - - - 503 - /WEB-INF/view/jsp/errors.jsp - - - - index.jsp - - diff --git a/cas-server-webapp/src/main/webapp/css/cas.css b/cas-server-webapp/src/main/webapp/css/cas.css deleted file mode 100644 index 246edd1d55..0000000000 --- a/cas-server-webapp/src/main/webapp/css/cas.css +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Licensed to Jasig under one or more contributor license - * agreements. See the NOTICE file distributed with this work - * for additional information regarding copyright ownership. - * Jasig licenses this file to you 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 the following location: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; outline: 0; font-size: 100%; vertical-align: baseline; background: transparent; } -body { line-height: 1; } -nav ul { list-style: none; } -blockquote, q { quotes: none; } -blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } -a { margin: 0; padding: 0; font-size: 100%; vertical-align: baseline; background: transparent; } -ins { background-color: #ff9; color: #000; text-decoration: none; } -mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; } -del { text-decoration: line-through; } -abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; } -table { border-collapse: collapse; border-spacing: 0; } -hr { display: block; height: 1px; border: 0; border-top: 1px solid #cccccc; margin: 1em 0; padding: 0; } -input, select { vertical-align: middle; } -body { font-family: Verdana, sans-serif; font-size: 11px; line-height: 1.4em; background: #eee; } - -#container { width: 95%; margin: 0 auto; } -header, #content { background: #fff; -webkit-box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.1); } -#content { -webkit-border-bottom-right-radius: 4px; -webkit-border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -moz-border-radius-bottomleft: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; } - -@media only screen and (max-width: 960px) { - #container { width: 100%; } - #content { -webkit-border-bottom-right-radius: 0px; -webkit-border-bottom-left-radius: 0px; -moz-border-radius-bottomright: 0px; -moz-border-radius-bottomleft: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; } -} - -header { padding-top: 10px; } -header #logo { display: block; width: 118px; height: 31px; text-indent: -999em; background: url(../images/ja-sig-logo.gif) no-repeat; margin: 0 0 10px 10px; } -header h1 { background: #210F7A; color: white; padding: 1.4em 1.4em; font-size: 2em; font-weight: normal; } - -#content { padding: 10px; overflow: hidden; } - -#msg { padding: 20px; margin-bottom: 10px; } -#msg h2 { font-size: 1.4em; margin-bottom: 1em; } -#msg.errors { border: 1px dotted #BB0000; color: #BB0000; padding-left: 100px; background: url(../images/error.gif) no-repeat 20px center; } -#msg.success { border: 1px dotted #390; color: #390; padding-left: 100px; background: url(../images/confirm.gif) no-repeat 20px center; } -#msg.info { border: 1px dotted #008; color: #008; padding-left: 100px; background: url(../images/info.gif) no-repeat 20px center; } -#msg.question { border: 1px dotted #390; color: #390; padding-left: 100px; background: url(../images/question.png) no-repeat 20px center; } -#msg.warn { border: 1px dotted #960; color: #960; padding-left: 100px; background: #ffbc8f url(../images/info.gif) no-repeat 20px center; } -#msg.warn .message { color: black; margin: 1em; font-size: 1.4em } - -#login { width: 320px; float: left; margin-right: 20px; } -#fm1 { padding: 20px; background: #eee; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } -#fm1 h2 { font-size: 1.4em; font-weight: normal; padding-bottom: 10px; margin-bottom: 10px; border-bottom: 1px solid #DDDDDD; } -#fm1 .row { margin-bottom: 10px; } -#fm1 .row label { display: block; color: #777777; } -#fm1 .row input[type=text], -#fm1 .row input[type=password] { padding: 6px; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; border: 1px solid #DDDDDD; background: #FFFFDD; } -#fm1 .row.check { padding-bottom: 10px; margin-bottom: 10px; border-bottom: 1px solid #DDDDDD; color: #777; font-size: 11px; } -#fm1 .row.check label { display: inline; } -#fm1 .row .btn-submit { border-width: 2px; padding: 3px; margin-right: 4px; } -#fm1 .row .btn-reset { border: 0; background: none; color: #777; text-transform: lowercase; border-left: 1px solid #ddd; } -#fm1 .row .btn-submit:hover, -#fm1 .row .btn-reset:hover { cursor: pointer; } - -#sidebar { width: auto; } -#sidebar .sidebar-content { padding-left: 20px; } -#sidebar .sidebar-content p { margin-bottom: 1.4em; } - -#list-languages ul { list-style: none; } -#list-languages ul li { display: inline-block; padding: 0px 10px; border-right: 1px solid #e2e2e2; } -#list-languages ul li:last-child { border: 0; line-height: 1.4em; } - -footer { color: #999; margin: 20px 0; } - -@media only screen and (max-width: 960px) { - footer { padding-left: 10px; } -} - -@media only screen and (max-width: 799px) { - header h1 { font-size: 1em; } - #login { float: none; width: 100%; } - #fm1 .row input[type=text], - #fm1 .row input[type=password] { width: 100%; padding: 10px; box-sizing: border-box; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; } - #fm1 .row .btn-submit { outline: none; -webkit-appearance: none; -webkit-border-radius: 0; border: 0; background: #210F7A; color: white; font-weight: bold; width: 100%; padding: 10px 20px; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } - #fm1 .row .btn-reset { display: none; } - #sidebar { margin-top: 20px; } - #sidebar .sidebar-content { padding: 0; } -} - -a.button { - font-weight:bold; - height:1.7em; - padding:1.2em 1em .6em 1em; - text-align:center; - text-decoration:none; - color:white; - background-color:#79946c; - border-top:1px solid #82b36c; - border-left:1px solid #82b36c; - border-right:1px solid #304228; - border-bottom:1px solid #304228; - -moz-border-radius: 3px; - border-radius: 3px; -} - -#big-buttons a.button { - display:block; - position:relative; - font-size: 1.6em; - margin:20px auto; - width:200px; - overflow: hidden; - -moz-border-radius: 7px; - border-radius: 7px; - border-top:2px solid #82b36c; - border-left:2px solid #82b36c; - border-right:2px solid #304228; - border-bottom:2px solid #304228; -} - -a.button:hover { - color:#ffff94; - background-color:#819e73; -} - -.button-down { - border-top:1px solid #304228; - border-left:1px solid #304228; - border-right:1px solid #82b36c; - border-bottom:1px solid #82b36c; -} - -#big-buttons .button-down { - border-top:2px solid #304228; - border-left:2px solid #304228; - border-right:2px solid #82b36c; - border-bottom:2px solid #82b36c; -} diff --git a/cas-server-webapp/src/main/webapp/favicon.ico b/cas-server-webapp/src/main/webapp/favicon.ico deleted file mode 100644 index 635bac544f..0000000000 Binary files a/cas-server-webapp/src/main/webapp/favicon.ico and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/images/confirm.gif b/cas-server-webapp/src/main/webapp/images/confirm.gif deleted file mode 100644 index 533bbba5c4..0000000000 Binary files a/cas-server-webapp/src/main/webapp/images/confirm.gif and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/images/error.gif b/cas-server-webapp/src/main/webapp/images/error.gif deleted file mode 100644 index b5f4c03064..0000000000 Binary files a/cas-server-webapp/src/main/webapp/images/error.gif and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/images/green.gif b/cas-server-webapp/src/main/webapp/images/green.gif deleted file mode 100644 index 8d4f802372..0000000000 Binary files a/cas-server-webapp/src/main/webapp/images/green.gif and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/images/info.gif b/cas-server-webapp/src/main/webapp/images/info.gif deleted file mode 100644 index 4aef02a906..0000000000 Binary files a/cas-server-webapp/src/main/webapp/images/info.gif and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/images/ja-sig-logo.gif b/cas-server-webapp/src/main/webapp/images/ja-sig-logo.gif deleted file mode 100644 index 0d67ad3d55..0000000000 Binary files a/cas-server-webapp/src/main/webapp/images/ja-sig-logo.gif and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/images/key-point_bl.gif b/cas-server-webapp/src/main/webapp/images/key-point_bl.gif deleted file mode 100644 index fe6f825706..0000000000 Binary files a/cas-server-webapp/src/main/webapp/images/key-point_bl.gif and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/images/key-point_br.gif b/cas-server-webapp/src/main/webapp/images/key-point_br.gif deleted file mode 100644 index ba5f51b83d..0000000000 Binary files a/cas-server-webapp/src/main/webapp/images/key-point_br.gif and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/images/key-point_tl.gif b/cas-server-webapp/src/main/webapp/images/key-point_tl.gif deleted file mode 100644 index 87d7cd0d0e..0000000000 Binary files a/cas-server-webapp/src/main/webapp/images/key-point_tl.gif and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/images/key-point_tr.gif b/cas-server-webapp/src/main/webapp/images/key-point_tr.gif deleted file mode 100644 index 602127a5ff..0000000000 Binary files a/cas-server-webapp/src/main/webapp/images/key-point_tr.gif and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/images/red.gif b/cas-server-webapp/src/main/webapp/images/red.gif deleted file mode 100644 index 8362124fe5..0000000000 Binary files a/cas-server-webapp/src/main/webapp/images/red.gif and /dev/null differ diff --git a/cas-server-webapp/src/main/webapp/index.jsp b/cas-server-webapp/src/main/webapp/index.jsp deleted file mode 100644 index d2914c529b..0000000000 --- a/cas-server-webapp/src/main/webapp/index.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you 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 the following location: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - ---%> -<%@ page language="java" session="false" %> - -<% -final String queryString = request.getQueryString(); -final String url = request.getContextPath() + "/login" + (queryString != null ? "?" + queryString : ""); -response.sendRedirect(response.encodeURL(url));%> diff --git a/cas-server-webapp/src/main/webapp/js/cas.js b/cas-server-webapp/src/main/webapp/js/cas.js deleted file mode 100644 index f12ae42bde..0000000000 --- a/cas-server-webapp/src/main/webapp/js/cas.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to Jasig under one or more contributor license - * agreements. See the NOTICE file distributed with this work - * for additional information regarding copyright ownership. - * Jasig licenses this file to you 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 the following location: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -$(document).ready(function(){ - //focus username field - $("input:visible:enabled:first").focus(); - //flash error box - $('#msg.errors').animate({ backgroundColor: 'rgb(187,0,0)' }, 30).animate({ backgroundColor: 'rgb(255,238,221)' }, 500); - - //flash success box - $('#msg.success').animate({ backgroundColor: 'rgb(51,204,0)' }, 30).animate({ backgroundColor: 'rgb(221,255,170)' }, 500); - - //flash confirm box - $('#msg.question').animate({ backgroundColor: 'rgb(51,204,0)' }, 30).animate({ backgroundColor: 'rgb(221,255,170)' }, 500); - - /* - * Using the JavaScript Debug library, you may issue log messages such as: - * debug.log("Welcome to Central Authentication Service"); - */ -}); diff --git a/catalogapp/.gitignore b/catalogapp/.gitignore deleted file mode 100644 index fbca71e503..0000000000 --- a/catalogapp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -src/main/webapp/WEB-INF/jsp/index.jsp diff --git a/catalogapp/README.md b/catalogapp/README.md deleted file mode 100644 index 285b4f7d69..0000000000 --- a/catalogapp/README.md +++ /dev/null @@ -1,30 +0,0 @@ -Catalogapp -========== - -Catalogapp is a simple and easy to use CSW client. - - -Parameters -========== - -The application accepts several GET parameters : - * **lang** can be set to any of the following : fr, en, es - * **debug** when set to true, the application loads unminified javascript files - * **noheader** when set to true, the application does not load the static header - - -How to run catalogapp without Tomcat ? -================================================ - -This mode is useful for **demo** or **development** purposes. - -The *first* time, you need to previously compile catalogapp and all its dependencies - - $ ./mvn -Dmaven.test.skip=true -Ptemplate -P-all,catalogapp install; - -then, each time you want to test a change in the configuration or the catalogapp module: - - $ cd catalogapp - $ ../mvn -Ptemplate jetty:run - -Point your browser to [http://localhost:8281/catalogapp/?noheader=true](http://localhost:8281/catalogapp/?noheader=true) diff --git a/catalogapp/jsbuild/build.bat b/catalogapp/jsbuild/build.bat deleted file mode 100644 index bcaa4f2b57..0000000000 --- a/catalogapp/jsbuild/build.bat +++ /dev/null @@ -1 +0,0 @@ -powershell %~dp0/build.ps1 \ No newline at end of file diff --git a/catalogapp/jsbuild/build.ps1 b/catalogapp/jsbuild/build.ps1 deleted file mode 100644 index fb073c4a3a..0000000000 --- a/catalogapp/jsbuild/build.ps1 +++ /dev/null @@ -1,54 +0,0 @@ -# -# Variables -# -$buildpath=split-path -parent $MyInvocation.MyCommand.Definition -$webapppath="$buildpath\..\src\main\webapp" -$releasepath="$webapppath\build" -$venv="$buildpath\venv" - -# -# build -# -if ( Test-Path -Path $releasepath ) -{ - rm -Recurse -Force $releasepath -} - -mkdir -Force $releasepath > $null -mkdir -Force $releasepath\lang > $null -$start=$(get-location) - -cd $buildpath; -if (!(Test-Path $venv)) -{ - echo "creating virtual env and installing jstools..." - $cmd = "cd $buildpath ; python go-jstools.py $venv --no-site-packages" - Start-Process "$psHome\powershell.exe" -Verb Runas -ArgumentList "-command $cmd" - echo "done." -} -echo "running jsbuild for main app..." -cmd /C $venv\Scripts\jsbuild -o "${releasepath}" main.cfg -echo "running jsbuild for edit app..." -cmd /C $venv\Scripts\jsbuild -o "${releasepath}" edit.cfg -echo "done." -cd $start - - -# -# OpenLayers resources -# -$openlayerspath="${webapppath}\lib\externals\openlayers" -$openlayersreleasepath="${releasepath}\openlayers" - -echo "copying OpenLayers resources..." -mkdir ${openlayersreleasepath} -cp -Force -Recurse "${openlayerspath}\img" "${openlayersreleasepath}" -cp -Force -Recurse "${openlayerspath}\theme" "${openlayersreleasepath}" -echo "done." - -# Cleanup SVN stuff -Get-ChildItem "${releasepath}" -include .svn -Recurse -Force | Remove-Item -Recurse -Force - -echo "built files and resources placed in src\main\webapp\build" - -exit 0 diff --git a/catalogapp/jsbuild/build.sh b/catalogapp/jsbuild/build.sh deleted file mode 100755 index aa9ec3a9b0..0000000000 --- a/catalogapp/jsbuild/build.sh +++ /dev/null @@ -1,63 +0,0 @@ -# -# Variables -# -buildpath="$(cd $(dirname $0); pwd)" -webapppath="${buildpath}/../src/main/webapp" -releasepath="${webapppath}/build" -venv="${buildpath}/env" - -# -# Command path definitions -# -python="/usr/bin/python" -mkdir="/bin/mkdir" -rm="/bin/rm" -sh="/bin/sh" -cp="/bin/cp" - -# -# build -# -if [ -d ${releasepath} ]; then - ${rm} -rf ${releasepath} -fi - -${mkdir} -p ${releasepath} ${releasepath}/lang - -(cd ${buildpath}; - ${venv}/bin/jsbuild -h > /dev/null - if [ ! -d ${venv} ] || [ $? -eq 0 ]; then - echo "creating virtual env and installing jstools..." - rm -rf ${venv} - virtualenv --no-site-packages ${venv} - ${venv}/bin/pip install jstools - echo "done." - fi; - - echo "running jsbuild for main app..." - ${venv}/bin/jsbuild -o "${releasepath}" main.cfg - echo "done." -) - -if [ ! -e ${releasepath}/catalogapp.js ]; then - echo "\033[01;31m[NOK]\033[00m jsbuild failure" - exit 1 -fi; -# -# OpenLayers resources -# -openlayerspath="${webapppath}/lib/externals/openlayers" -openlayersreleasepath="${releasepath}/openlayers" - -echo "copying OpenLayers resources..." -${mkdir} ${openlayersreleasepath} -${cp} -r "${openlayerspath}/img" "${openlayersreleasepath}" -${cp} -r "${openlayerspath}/theme" "${openlayersreleasepath}" -echo "done." - -# Cleanup SVN stuff -${rm} -rf `find "${releasepath}" -name .svn -type d` - -echo "built files and resources placed in src/main/webapp/build" - -exit 0 \ No newline at end of file diff --git a/catalogapp/jsbuild/go-jstools.py b/catalogapp/jsbuild/go-jstools.py deleted file mode 100644 index 9240f67177..0000000000 --- a/catalogapp/jsbuild/go-jstools.py +++ /dev/null @@ -1,1523 +0,0 @@ -#!/usr/bin/env python -## WARNING: This file is generated -#!/usr/bin/env python -"""Create a "virtual" Python installation -""" - -virtualenv_version = "1.4.9" - -import sys -import os -import optparse -import re -import shutil -import logging -import distutils.sysconfig -try: - import subprocess -except ImportError, e: - if sys.version_info <= (2, 3): - print 'ERROR: %s' % e - print 'ERROR: this script requires Python 2.4 or greater; or at least the subprocess module.' - print 'If you copy subprocess.py from a newer version of Python this script will probably work' - sys.exit(101) - else: - raise -try: - set -except NameError: - from sets import Set as set - -join = os.path.join -py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1]) -is_jython = sys.platform.startswith('java') -expected_exe = is_jython and 'jython' or 'python' - -REQUIRED_MODULES = ['os', 'posix', 'posixpath', 'nt', 'ntpath', 'genericpath', - 'fnmatch', 'locale', 'encodings', 'codecs', - 'stat', 'UserDict', 'readline', 'copy_reg', 'types', - 're', 'sre', 'sre_parse', 'sre_constants', 'sre_compile', - 'lib-dynload', 'config', 'zlib'] - -if sys.version_info[:2] >= (2, 7): - REQUIRED_MODULES.extend(['_weakrefset']) -if sys.version_info[:2] >= (2, 6): - REQUIRED_MODULES.extend(['warnings', 'linecache', '_abcoll', 'abc']) -if sys.version_info[:2] <= (2, 3): - REQUIRED_MODULES.extend(['sets', '__future__']) - -class Logger(object): - - """ - Logging object for use in command-line script. Allows ranges of - levels, to avoid some redundancy of displayed information. - """ - - DEBUG = logging.DEBUG - INFO = logging.INFO - NOTIFY = (logging.INFO+logging.WARN)/2 - WARN = WARNING = logging.WARN - ERROR = logging.ERROR - FATAL = logging.FATAL - - LEVELS = [DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL] - - def __init__(self, consumers): - self.consumers = consumers - self.indent = 0 - self.in_progress = None - self.in_progress_hanging = False - - def debug(self, msg, *args, **kw): - self.log(self.DEBUG, msg, *args, **kw) - def info(self, msg, *args, **kw): - self.log(self.INFO, msg, *args, **kw) - def notify(self, msg, *args, **kw): - self.log(self.NOTIFY, msg, *args, **kw) - def warn(self, msg, *args, **kw): - self.log(self.WARN, msg, *args, **kw) - def error(self, msg, *args, **kw): - self.log(self.WARN, msg, *args, **kw) - def fatal(self, msg, *args, **kw): - self.log(self.FATAL, msg, *args, **kw) - def log(self, level, msg, *args, **kw): - if args: - if kw: - raise TypeError( - "You may give positional or keyword arguments, not both") - args = args or kw - rendered = None - for consumer_level, consumer in self.consumers: - if self.level_matches(level, consumer_level): - if (self.in_progress_hanging - and consumer in (sys.stdout, sys.stderr)): - self.in_progress_hanging = False - sys.stdout.write('\n') - sys.stdout.flush() - if rendered is None: - if args: - rendered = msg % args - else: - rendered = msg - rendered = ' '*self.indent + rendered - if hasattr(consumer, 'write'): - consumer.write(rendered+'\n') - else: - consumer(rendered) - - def start_progress(self, msg): - assert not self.in_progress, ( - "Tried to start_progress(%r) while in_progress %r" - % (msg, self.in_progress)) - if self.level_matches(self.NOTIFY, self._stdout_level()): - sys.stdout.write(msg) - sys.stdout.flush() - self.in_progress_hanging = True - else: - self.in_progress_hanging = False - self.in_progress = msg - - def end_progress(self, msg='done.'): - assert self.in_progress, ( - "Tried to end_progress without start_progress") - if self.stdout_level_matches(self.NOTIFY): - if not self.in_progress_hanging: - # Some message has been printed out since start_progress - sys.stdout.write('...' + self.in_progress + msg + '\n') - sys.stdout.flush() - else: - sys.stdout.write(msg + '\n') - sys.stdout.flush() - self.in_progress = None - self.in_progress_hanging = False - - def show_progress(self): - """If we are in a progress scope, and no log messages have been - shown, write out another '.'""" - if self.in_progress_hanging: - sys.stdout.write('.') - sys.stdout.flush() - - def stdout_level_matches(self, level): - """Returns true if a message at this level will go to stdout""" - return self.level_matches(level, self._stdout_level()) - - def _stdout_level(self): - """Returns the level that stdout runs at""" - for level, consumer in self.consumers: - if consumer is sys.stdout: - return level - return self.FATAL - - def level_matches(self, level, consumer_level): - """ - >>> l = Logger() - >>> l.level_matches(3, 4) - False - >>> l.level_matches(3, 2) - True - >>> l.level_matches(slice(None, 3), 3) - False - >>> l.level_matches(slice(None, 3), 2) - True - >>> l.level_matches(slice(1, 3), 1) - True - >>> l.level_matches(slice(2, 3), 1) - False - """ - if isinstance(level, slice): - start, stop = level.start, level.stop - if start is not None and start > consumer_level: - return False - if stop is not None or stop <= consumer_level: - return False - return True - else: - return level >= consumer_level - - #@classmethod - def level_for_integer(cls, level): - levels = cls.LEVELS - if level < 0: - return levels[0] - if level >= len(levels): - return levels[-1] - return levels[level] - - level_for_integer = classmethod(level_for_integer) - -def mkdir(path): - if not os.path.exists(path): - logger.info('Creating %s', path) - os.makedirs(path) - else: - logger.info('Directory %s already exists', path) - -def copyfile(src, dest, symlink=True): - if not os.path.exists(src): - # Some bad symlink in the src - logger.warn('Cannot find file %s (bad symlink)', src) - return - if os.path.exists(dest): - logger.debug('File %s already exists', dest) - return - if not os.path.exists(os.path.dirname(dest)): - logger.info('Creating parent directories for %s' % os.path.dirname(dest)) - os.makedirs(os.path.dirname(dest)) - if symlink and hasattr(os, 'symlink'): - logger.info('Symlinking %s', dest) - os.symlink(os.path.abspath(src), dest) - else: - logger.info('Copying to %s', dest) - if os.path.isdir(src): - shutil.copytree(src, dest, True) - else: - shutil.copy2(src, dest) - -def writefile(dest, content, overwrite=True): - if not os.path.exists(dest): - logger.info('Writing %s', dest) - f = open(dest, 'wb') - f.write(content) - f.close() - return - else: - f = open(dest, 'rb') - c = f.read() - f.close() - if c != content: - if not overwrite: - logger.notify('File %s exists with different content; not overwriting', dest) - return - logger.notify('Overwriting %s with new content', dest) - f = open(dest, 'wb') - f.write(content) - f.close() - else: - logger.info('Content %s already in place', dest) - -def rmtree(dir): - if os.path.exists(dir): - logger.notify('Deleting tree %s', dir) - shutil.rmtree(dir) - else: - logger.info('Do not need to delete %s; already gone', dir) - -def make_exe(fn): - if hasattr(os, 'chmod'): - oldmode = os.stat(fn).st_mode & 07777 - newmode = (oldmode | 0555) & 07777 - os.chmod(fn, newmode) - logger.info('Changed mode of %s to %s', fn, oct(newmode)) - -def _find_file(filename, dirs): - for dir in dirs: - if os.path.exists(join(dir, filename)): - return join(dir, filename) - return filename - -def _install_req(py_executable, unzip=False, distribute=False): - if not distribute: - setup_fn = 'setuptools-0.6c11-py%s.egg' % sys.version[:3] - project_name = 'setuptools' - bootstrap_script = EZ_SETUP_PY - source = None - else: - setup_fn = None - source = 'distribute-0.6.8.tar.gz' - project_name = 'distribute' - bootstrap_script = DISTRIBUTE_SETUP_PY - try: - # check if the global Python has distribute installed or plain - # setuptools - import pkg_resources - if not hasattr(pkg_resources, '_distribute'): - location = os.path.dirname(pkg_resources.__file__) - logger.notify("A globally installed setuptools was found (in %s)" % location) - logger.notify("Use the --no-site-packages option to use distribute in " - "the virtualenv.") - except ImportError: - pass - - search_dirs = file_search_dirs() - - if setup_fn is not None: - setup_fn = _find_file(setup_fn, search_dirs) - - if source is not None: - source = _find_file(source, search_dirs) - - if is_jython and os._name == 'nt': - # Jython's .bat sys.executable can't handle a command line - # argument with newlines - import tempfile - fd, ez_setup = tempfile.mkstemp('.py') - os.write(fd, bootstrap_script) - os.close(fd) - cmd = [py_executable, ez_setup] - else: - cmd = [py_executable, '-c', bootstrap_script] - if unzip: - cmd.append('--always-unzip') - env = {} - if logger.stdout_level_matches(logger.DEBUG): - cmd.append('-v') - - old_chdir = os.getcwd() - if setup_fn is not None and os.path.exists(setup_fn): - logger.info('Using existing %s egg: %s' % (project_name, setup_fn)) - cmd.append(setup_fn) - if os.environ.get('PYTHONPATH'): - env['PYTHONPATH'] = setup_fn + os.path.pathsep + os.environ['PYTHONPATH'] - else: - env['PYTHONPATH'] = setup_fn - else: - # the source is found, let's chdir - if source is not None and os.path.exists(source): - os.chdir(os.path.dirname(source)) - else: - logger.info('No %s egg found; downloading' % project_name) - cmd.extend(['--always-copy', '-U', project_name]) - logger.start_progress('Installing %s...' % project_name) - logger.indent += 2 - cwd = None - if project_name == 'distribute': - env['DONT_PATCH_SETUPTOOLS'] = 'true' - - def _filter_ez_setup(line): - return filter_ez_setup(line, project_name) - - if not os.access(os.getcwd(), os.W_OK): - cwd = '/tmp' - if source is not None and os.path.exists(source): - # the current working dir is hostile, let's copy the - # tarball to /tmp - target = os.path.join(cwd, os.path.split(source)[-1]) - shutil.copy(source, target) - try: - call_subprocess(cmd, show_stdout=False, - filter_stdout=_filter_ez_setup, - extra_env=env, - cwd=cwd) - finally: - logger.indent -= 2 - logger.end_progress() - if os.getcwd() != old_chdir: - os.chdir(old_chdir) - if is_jython and os._name == 'nt': - os.remove(ez_setup) - -def file_search_dirs(): - here = os.path.dirname(os.path.abspath(__file__)) - dirs = ['.', here, - #join(here, 'virtualenv_support')] - '/usr/share/python-virtualenv/'] - if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv': - # Probably some boot script; just in case virtualenv is installed... - try: - import virtualenv - except ImportError: - pass - else: - dirs.append(os.path.join(os.path.dirname(virtualenv.__file__), 'virtualenv_support')) - return [d for d in dirs if os.path.isdir(d)] - -def install_setuptools(py_executable, unzip=False): - _install_req(py_executable, unzip) - -def install_distribute(py_executable, unzip=False): - _install_req(py_executable, unzip, distribute=True) - -_pip_re = re.compile(r'^pip-.*(zip|tar.gz|tar.bz2|tgz|tbz)$', re.I) -def install_pip(py_executable): - filenames = [] - for dir in file_search_dirs(): - filenames.extend([join(dir, fn) for fn in os.listdir(dir) - if _pip_re.search(fn)]) - filenames.sort(key=lambda x: os.path.basename(x).lower()) - if not filenames: - filename = 'pip' - else: - filename = filenames[-1] - easy_install_script = 'easy_install' - if sys.platform == 'win32': - easy_install_script = 'easy_install-script.py' - cmd = [py_executable, join(os.path.dirname(py_executable), easy_install_script), filename] - if filename == 'pip': - logger.info('Installing pip from network...') - else: - logger.info('Installing %s' % os.path.basename(filename)) - logger.indent += 2 - def _filter_setup(line): - return filter_ez_setup(line, 'pip') - try: - call_subprocess(cmd, show_stdout=False, - filter_stdout=_filter_setup) - finally: - logger.indent -= 2 - -def filter_ez_setup(line, project_name='setuptools'): - if not line.strip(): - return Logger.DEBUG - if project_name == 'distribute': - for prefix in ('Extracting', 'Now working', 'Installing', 'Before', - 'Scanning', 'Setuptools', 'Egg', 'Already', - 'running', 'writing', 'reading', 'installing', - 'creating', 'copying', 'byte-compiling', 'removing', - 'Processing'): - if line.startswith(prefix): - return Logger.DEBUG - return Logger.DEBUG - for prefix in ['Reading ', 'Best match', 'Processing setuptools', - 'Copying setuptools', 'Adding setuptools', - 'Installing ', 'Installed ']: - if line.startswith(prefix): - return Logger.DEBUG - return Logger.INFO - -def main(): - parser = optparse.OptionParser( - version=virtualenv_version, - usage="%prog [OPTIONS] DEST_DIR") - - parser.add_option( - '-v', '--verbose', - action='count', - dest='verbose', - default=0, - help="Increase verbosity") - - parser.add_option( - '-q', '--quiet', - action='count', - dest='quiet', - default=0, - help='Decrease verbosity') - - parser.add_option( - '-p', '--python', - dest='python', - metavar='PYTHON_EXE', - help='The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 ' - 'interpreter to create the new environment. The default is the interpreter that ' - 'virtualenv was installed with (%s)' % sys.executable) - - parser.add_option( - '--clear', - dest='clear', - action='store_true', - help="Clear out the non-root install and start from scratch") - - parser.add_option( - '--no-site-packages', - dest='no_site_packages', - action='store_true', - help="Don't give access to the global site-packages dir to the " - "virtual environment") - - parser.add_option( - '--unzip-setuptools', - dest='unzip_setuptools', - action='store_true', - help="Unzip Setuptools or Distribute when installing it") - - parser.add_option( - '--relocatable', - dest='relocatable', - action='store_true', - help='Make an EXISTING virtualenv environment relocatable. ' - 'This fixes up scripts and makes all .pth files relative') - - parser.add_option( - '--distribute', - dest='use_distribute', - action='store_true', default=True, - help='Ignored. Distribute is used by default. See --setuptools ' - 'to use Setuptools instead of Distribute.') - - parser.add_option( - '--setuptools', - dest='use_distribute', - action='store_false', - help='Use Setuptools instead of Distribute. Set environ variable ' - 'VIRTUALENV_USE_SETUPTOOLS to make it the default.') - - if 'extend_parser' in globals(): - extend_parser(parser) - - options, args = parser.parse_args() - - global logger - - if 'adjust_options' in globals(): - adjust_options(options, args) - - verbosity = options.verbose - options.quiet - logger = Logger([(Logger.level_for_integer(2-verbosity), sys.stdout)]) - - if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'): - env = os.environ.copy() - interpreter = resolve_interpreter(options.python) - if interpreter == sys.executable: - logger.warn('Already using interpreter %s' % interpreter) - else: - logger.notify('Running virtualenv with interpreter %s' % interpreter) - env['VIRTUALENV_INTERPRETER_RUNNING'] = 'true' - file = __file__ - if file.endswith('.pyc'): - file = file[:-1] - os.execvpe(interpreter, [interpreter, file] + sys.argv[1:], env) - - if not args: - print 'You must provide a DEST_DIR' - parser.print_help() - sys.exit(2) - if len(args) > 1: - print 'There must be only one argument: DEST_DIR (you gave %s)' % ( - ' '.join(args)) - parser.print_help() - sys.exit(2) - - home_dir = args[0] - - if os.environ.get('WORKING_ENV'): - logger.fatal('ERROR: you cannot run virtualenv while in a workingenv') - logger.fatal('Please deactivate your workingenv, then re-run this script') - sys.exit(3) - - if 'PYTHONHOME' in os.environ: - logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it') - del os.environ['PYTHONHOME'] - - if options.relocatable: - make_environment_relocatable(home_dir) - return - - create_environment(home_dir, site_packages=not options.no_site_packages, clear=options.clear, - unzip_setuptools=options.unzip_setuptools, - use_distribute=options.use_distribute) - if 'after_install' in globals(): - after_install(options, home_dir) - -def call_subprocess(cmd, show_stdout=True, - filter_stdout=None, cwd=None, - raise_on_returncode=True, extra_env=None): - cmd_parts = [] - for part in cmd: - if len(part) > 40: - part = part[:30]+"..."+part[-5:] - if ' ' in part or '\n' in part or '"' in part or "'" in part: - part = '"%s"' % part.replace('"', '\\"') - cmd_parts.append(part) - cmd_desc = ' '.join(cmd_parts) - if show_stdout: - stdout = None - else: - stdout = subprocess.PIPE - logger.debug("Running command %s" % cmd_desc) - if extra_env: - env = os.environ.copy() - env.update(extra_env) - else: - env = None - try: - proc = subprocess.Popen( - cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, - cwd=cwd, env=env) - except Exception, e: - logger.fatal( - "Error %s while executing command %s" % (e, cmd_desc)) - raise - all_output = [] - if stdout is not None: - stdout = proc.stdout - while 1: - line = stdout.readline() - if not line: - break - line = line.rstrip() - all_output.append(line) - if filter_stdout: - level = filter_stdout(line) - if isinstance(level, tuple): - level, line = level - logger.log(level, line) - if not logger.stdout_level_matches(level): - logger.show_progress() - else: - logger.info(line) - else: - proc.communicate() - proc.wait() - if proc.returncode: - if raise_on_returncode: - if all_output: - logger.notify('Complete output from command %s:' % cmd_desc) - logger.notify('\n'.join(all_output) + '\n----------------------------------------') - raise OSError( - "Command %s failed with error code %s" - % (cmd_desc, proc.returncode)) - else: - logger.warn( - "Command %s had error code %s" - % (cmd_desc, proc.returncode)) - - -def create_environment(home_dir, site_packages=True, clear=False, - unzip_setuptools=False, use_distribute=True): - """ - Creates a new environment in ``home_dir``. - - If ``site_packages`` is true (the default) then the global - ``site-packages/`` directory will be on the path. - - If ``clear`` is true (default False) then the environment will - first be cleared. - """ - home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) - - py_executable = os.path.abspath(install_python( - home_dir, lib_dir, inc_dir, bin_dir, - site_packages=site_packages, clear=clear)) - - install_distutils(lib_dir, home_dir) - - if not use_distribute or os.environ.get('VIRTUALENV_USE_SETUPTOOLS'): - install_setuptools(py_executable, unzip=unzip_setuptools) - else: - install_distribute(py_executable, unzip=unzip_setuptools) - - install_pip(py_executable) - - install_activate(home_dir, bin_dir) - -def path_locations(home_dir): - """Return the path locations for the environment (where libraries are, - where scripts go, etc)""" - # XXX: We'd use distutils.sysconfig.get_python_inc/lib but its - # prefix arg is broken: http://bugs.python.org/issue3386 - if sys.platform == 'win32': - # Windows has lots of problems with executables with spaces in - # the name; this function will remove them (using the ~1 - # format): - mkdir(home_dir) - if ' ' in home_dir: - try: - import win32api - except ImportError: - print 'Error: the path "%s" has a space in it' % home_dir - print 'To handle these kinds of paths, the win32api module must be installed:' - print ' http://sourceforge.net/projects/pywin32/' - sys.exit(3) - home_dir = win32api.GetShortPathName(home_dir) - lib_dir = join(home_dir, 'Lib') - inc_dir = join(home_dir, 'Include') - bin_dir = join(home_dir, 'Scripts') - elif is_jython: - lib_dir = join(home_dir, 'Lib') - inc_dir = join(home_dir, 'Include') - bin_dir = join(home_dir, 'bin') - else: - lib_dir = join(home_dir, 'lib', py_version) - inc_dir = join(home_dir, 'include', py_version) - bin_dir = join(home_dir, 'bin') - return home_dir, lib_dir, inc_dir, bin_dir - -def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear): - """Install just the base environment, no distutils patches etc""" - if sys.executable.startswith(bin_dir): - print 'Please use the *system* python to run this script' - return - - if clear: - rmtree(lib_dir) - ## FIXME: why not delete it? - ## Maybe it should delete everything with #!/path/to/venv/python in it - logger.notify('Not deleting %s', bin_dir) - - if hasattr(sys, 'real_prefix'): - logger.notify('Using real prefix %r' % sys.real_prefix) - prefix = sys.real_prefix - else: - prefix = sys.prefix - mkdir(lib_dir) - fix_lib64(lib_dir) - stdlib_dirs = [os.path.dirname(os.__file__)] - if sys.platform == 'win32': - stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), 'DLLs')) - elif sys.platform == 'darwin': - stdlib_dirs.append(join(stdlib_dirs[0], 'site-packages')) - for stdlib_dir in stdlib_dirs: - if not os.path.isdir(stdlib_dir): - continue - if hasattr(os, 'symlink'): - logger.info('Symlinking Python bootstrap modules') - else: - logger.info('Copying Python bootstrap modules') - logger.indent += 2 - try: - for fn in os.listdir(stdlib_dir): - if fn != 'site-packages' and os.path.splitext(fn)[0] in REQUIRED_MODULES: - copyfile(join(stdlib_dir, fn), join(lib_dir, fn)) - finally: - logger.indent -= 2 - mkdir(join(lib_dir, 'site-packages')) - writefile(join(lib_dir, 'site.py'), SITE_PY) - writefile(join(lib_dir, 'orig-prefix.txt'), prefix) - site_packages_filename = join(lib_dir, 'no-global-site-packages.txt') - if not site_packages: - writefile(site_packages_filename, '') - else: - if os.path.exists(site_packages_filename): - logger.info('Deleting %s' % site_packages_filename) - os.unlink(site_packages_filename) - - stdinc_dir = join(prefix, 'include', py_version) - if os.path.exists(stdinc_dir): - copyfile(stdinc_dir, inc_dir) - else: - logger.debug('No include dir %s' % stdinc_dir) - - if sys.exec_prefix != prefix: - if sys.platform == 'win32': - exec_dir = join(sys.exec_prefix, 'lib') - elif is_jython: - exec_dir = join(sys.exec_prefix, 'Lib') - else: - exec_dir = join(sys.exec_prefix, 'lib', py_version) - for fn in os.listdir(exec_dir): - copyfile(join(exec_dir, fn), join(lib_dir, fn)) - - if is_jython: - # Jython has either jython-dev.jar and javalib/ dir, or just - # jython.jar - for name in 'jython-dev.jar', 'javalib', 'jython.jar': - src = join(prefix, name) - if os.path.exists(src): - copyfile(src, join(home_dir, name)) - # XXX: registry should always exist after Jython 2.5rc1 - src = join(prefix, 'registry') - if os.path.exists(src): - copyfile(src, join(home_dir, 'registry'), symlink=False) - copyfile(join(prefix, 'cachedir'), join(home_dir, 'cachedir'), - symlink=False) - - mkdir(bin_dir) - py_executable = join(bin_dir, os.path.basename(sys.executable)) - if 'Python.framework' in prefix: - if re.search(r'/Python(?:-32|-64)*$', py_executable): - # The name of the python executable is not quite what - # we want, rename it. - py_executable = os.path.join( - os.path.dirname(py_executable), 'python') - - logger.notify('New %s executable in %s', expected_exe, py_executable) - if sys.executable != py_executable: - ## FIXME: could I just hard link? - executable = sys.executable - if sys.platform == 'cygwin' and os.path.exists(executable + '.exe'): - # Cygwin misreports sys.executable sometimes - executable += '.exe' - py_executable += '.exe' - logger.info('Executable actually exists in %s' % executable) - shutil.copyfile(executable, py_executable) - make_exe(py_executable) - if sys.platform == 'win32' or sys.platform == 'cygwin': - pythonw = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe') - if os.path.exists(pythonw): - logger.info('Also created pythonw.exe') - shutil.copyfile(pythonw, os.path.join(os.path.dirname(py_executable), 'pythonw.exe')) - - if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe: - secondary_exe = os.path.join(os.path.dirname(py_executable), - expected_exe) - py_executable_ext = os.path.splitext(py_executable)[1] - if py_executable_ext == '.exe': - # python2.4 gives an extension of '.4' :P - secondary_exe += py_executable_ext - if os.path.exists(secondary_exe): - logger.warn('Not overwriting existing %s script %s (you must use %s)' - % (expected_exe, secondary_exe, py_executable)) - else: - logger.notify('Also creating executable in %s' % secondary_exe) - shutil.copyfile(sys.executable, secondary_exe) - make_exe(secondary_exe) - - if 'Python.framework' in prefix: - logger.debug('MacOSX Python framework detected') - - # Make sure we use the the embedded interpreter inside - # the framework, even if sys.executable points to - # the stub executable in ${sys.prefix}/bin - # See http://groups.google.com/group/python-virtualenv/ - # browse_thread/thread/17cab2f85da75951 - shutil.copy( - os.path.join( - prefix, 'Resources/Python.app/Contents/MacOS/%s' % os.path.basename(sys.executable)), - py_executable) - - # Copy the framework's dylib into the virtual - # environment - virtual_lib = os.path.join(home_dir, '.Python') - - if os.path.exists(virtual_lib): - os.unlink(virtual_lib) - copyfile( - os.path.join(prefix, 'Python'), - virtual_lib) - - # And then change the install_name of the copied python executable - try: - call_subprocess( - ["install_name_tool", "-change", - os.path.join(prefix, 'Python'), - '@executable_path/../.Python', - py_executable]) - except: - logger.fatal( - "Could not call install_name_tool -- you must have Apple's development tools installed") - raise - - # Some tools depend on pythonX.Y being present - py_executable_version = '%s.%s' % ( - sys.version_info[0], sys.version_info[1]) - if not py_executable.endswith(py_executable_version): - # symlinking pythonX.Y > python - pth = py_executable + '%s.%s' % ( - sys.version_info[0], sys.version_info[1]) - if os.path.exists(pth): - os.unlink(pth) - os.symlink('python', pth) - else: - # reverse symlinking python -> pythonX.Y (with --python) - pth = join(bin_dir, 'python') - if os.path.exists(pth): - os.unlink(pth) - os.symlink(os.path.basename(py_executable), pth) - - if sys.platform == 'win32' and ' ' in py_executable: - # There's a bug with subprocess on Windows when using a first - # argument that has a space in it. Instead we have to quote - # the value: - py_executable = '"%s"' % py_executable - cmd = [py_executable, '-c', 'import sys; print sys.prefix'] - logger.info('Testing executable with %s %s "%s"' % tuple(cmd)) - proc = subprocess.Popen(cmd, - stdout=subprocess.PIPE) - proc_stdout, proc_stderr = proc.communicate() - proc_stdout = os.path.normcase(os.path.abspath(proc_stdout.strip())) - if proc_stdout != os.path.normcase(os.path.abspath(home_dir)): - logger.fatal( - 'ERROR: The executable %s is not functioning' % py_executable) - logger.fatal( - 'ERROR: It thinks sys.prefix is %r (should be %r)' - % (proc_stdout, os.path.normcase(os.path.abspath(home_dir)))) - logger.fatal( - 'ERROR: virtualenv is not compatible with this system or executable') - if sys.platform == 'win32': - logger.fatal( - 'Note: some Windows users have reported this error when they installed Python for "Only this user". The problem may be resolvable if you install Python "For all users". (See https://bugs.launchpad.net/virtualenv/+bug/352844)') - sys.exit(100) - else: - logger.info('Got sys.prefix result: %r' % proc_stdout) - - pydistutils = os.path.expanduser('~/.pydistutils.cfg') - if os.path.exists(pydistutils): - logger.notify('Please make sure you remove any previous custom paths from ' - 'your %s file.' % pydistutils) - ## FIXME: really this should be calculated earlier - return py_executable - -def install_activate(home_dir, bin_dir): - if sys.platform == 'win32' or is_jython and os._name == 'nt': - files = {'activate.bat': ACTIVATE_BAT, - 'deactivate.bat': DEACTIVATE_BAT} - if os.environ.get('OS') == 'Windows_NT' and os.environ.get('OSTYPE') == 'cygwin': - files['activate'] = ACTIVATE_SH - else: - files = {'activate': ACTIVATE_SH} - files['activate_this.py'] = ACTIVATE_THIS - for name, content in files.items(): - content = content.replace('__VIRTUAL_ENV__', os.path.abspath(home_dir)) - content = content.replace('__VIRTUAL_NAME__', os.path.basename(os.path.abspath(home_dir))) - content = content.replace('__BIN_NAME__', os.path.basename(bin_dir)) - writefile(os.path.join(bin_dir, name), content) - -def install_distutils(lib_dir, home_dir): - distutils_path = os.path.join(lib_dir, 'distutils') - mkdir(distutils_path) - ## FIXME: maybe this prefix setting should only be put in place if - ## there's a local distutils.cfg with a prefix setting? - home_dir = os.path.abspath(home_dir) - ## FIXME: this is breaking things, removing for now: - #distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" % home_dir - writefile(os.path.join(distutils_path, '__init__.py'), DISTUTILS_INIT) - writefile(os.path.join(distutils_path, 'distutils.cfg'), DISTUTILS_CFG, overwrite=False) - -def fix_lib64(lib_dir): - """ - Some platforms (particularly Gentoo on x64) put things in lib64/pythonX.Y - instead of lib/pythonX.Y. If this is such a platform we'll just create a - symlink so lib64 points to lib - """ - if [p for p in distutils.sysconfig.get_config_vars().values() - if isinstance(p, basestring) and 'lib64' in p]: - logger.debug('This system uses lib64; symlinking lib64 to lib') - assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], ( - "Unexpected python lib dir: %r" % lib_dir) - lib_parent = os.path.dirname(lib_dir) - assert os.path.basename(lib_parent) == 'lib', ( - "Unexpected parent dir: %r" % lib_parent) - copyfile(lib_parent, os.path.join(os.path.dirname(lib_parent), 'lib64')) - -def resolve_interpreter(exe): - """ - If the executable given isn't an absolute path, search $PATH for the interpreter - """ - if os.path.abspath(exe) != exe: - paths = os.environ.get('PATH', '').split(os.pathsep) - for path in paths: - if os.path.exists(os.path.join(path, exe)): - exe = os.path.join(path, exe) - break - if not os.path.exists(exe): - logger.fatal('The executable %s (from --python=%s) does not exist' % (exe, exe)) - sys.exit(3) - return exe - -############################################################ -## Relocating the environment: - -def make_environment_relocatable(home_dir): - """ - Makes the already-existing environment use relative paths, and takes out - the #!-based environment selection in scripts. - """ - activate_this = os.path.join(home_dir, 'bin', 'activate_this.py') - if not os.path.exists(activate_this): - logger.fatal( - 'The environment doesn\'t have a file %s -- please re-run virtualenv ' - 'on this environment to update it' % activate_this) - fixup_scripts(home_dir) - fixup_pth_and_egg_link(home_dir) - ## FIXME: need to fix up distutils.cfg - -OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3], - 'activate', 'activate.bat', 'activate_this.py'] - -def fixup_scripts(home_dir): - # This is what we expect at the top of scripts: - shebang = '#!%s/bin/python' % os.path.normcase(os.path.abspath(home_dir)) - # This is what we'll put: - new_shebang = '#!/usr/bin/env python%s' % sys.version[:3] - activate = "import os; activate_this=os.path.join(os.path.dirname(__file__), 'activate_this.py'); execfile(activate_this, dict(__file__=activate_this)); del os, activate_this" - bin_dir = os.path.join(home_dir, 'bin') - for filename in os.listdir(bin_dir): - filename = os.path.join(bin_dir, filename) - if not os.path.isfile(filename): - # ignore subdirs, e.g. .svn ones. - continue - f = open(filename, 'rb') - lines = f.readlines() - f.close() - if not lines: - logger.warn('Script %s is an empty file' % filename) - continue - if not lines[0].strip().startswith(shebang): - if os.path.basename(filename) in OK_ABS_SCRIPTS: - logger.debug('Cannot make script %s relative' % filename) - elif lines[0].strip() == new_shebang: - logger.info('Script %s has already been made relative' % filename) - else: - logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)' - % (filename, shebang)) - continue - logger.notify('Making script %s relative' % filename) - lines = [new_shebang+'\n', activate+'\n'] + lines[1:] - f = open(filename, 'wb') - f.writelines(lines) - f.close() - -def fixup_pth_and_egg_link(home_dir, sys_path=None): - """Makes .pth and .egg-link files use relative paths""" - home_dir = os.path.normcase(os.path.abspath(home_dir)) - if sys_path is None: - sys_path = sys.path - for path in sys_path: - if not path: - path = '.' - if not os.path.isdir(path): - continue - path = os.path.normcase(os.path.abspath(path)) - if not path.startswith(home_dir): - logger.debug('Skipping system (non-environment) directory %s' % path) - continue - for filename in os.listdir(path): - filename = os.path.join(path, filename) - if filename.endswith('.pth'): - if not os.access(filename, os.W_OK): - logger.warn('Cannot write .pth file %s, skipping' % filename) - else: - fixup_pth_file(filename) - if filename.endswith('.egg-link'): - if not os.access(filename, os.W_OK): - logger.warn('Cannot write .egg-link file %s, skipping' % filename) - else: - fixup_egg_link(filename) - -def fixup_pth_file(filename): - lines = [] - prev_lines = [] - f = open(filename) - prev_lines = f.readlines() - f.close() - for line in prev_lines: - line = line.strip() - if (not line or line.startswith('#') or line.startswith('import ') - or os.path.abspath(line) != line): - lines.append(line) - else: - new_value = make_relative_path(filename, line) - if line != new_value: - logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename)) - lines.append(new_value) - if lines == prev_lines: - logger.info('No changes to .pth file %s' % filename) - return - logger.notify('Making paths in .pth file %s relative' % filename) - f = open(filename, 'w') - f.write('\n'.join(lines) + '\n') - f.close() - -def fixup_egg_link(filename): - f = open(filename) - link = f.read().strip() - f.close() - if os.path.abspath(link) != link: - logger.debug('Link in %s already relative' % filename) - return - new_link = make_relative_path(filename, link) - logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link)) - f = open(filename, 'w') - f.write(new_link) - f.close() - -def make_relative_path(source, dest, dest_is_directory=True): - """ - Make a filename relative, where the filename is dest, and it is - being referred to from the filename source. - - >>> make_relative_path('/usr/share/something/a-file.pth', - ... '/usr/share/another-place/src/Directory') - '../another-place/src/Directory' - >>> make_relative_path('/usr/share/something/a-file.pth', - ... '/home/user/src/Directory') - '../../../home/user/src/Directory' - >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/') - './' - """ - source = os.path.dirname(source) - if not dest_is_directory: - dest_filename = os.path.basename(dest) - dest = os.path.dirname(dest) - dest = os.path.normpath(os.path.abspath(dest)) - source = os.path.normpath(os.path.abspath(source)) - dest_parts = dest.strip(os.path.sep).split(os.path.sep) - source_parts = source.strip(os.path.sep).split(os.path.sep) - while dest_parts and source_parts and dest_parts[0] == source_parts[0]: - dest_parts.pop(0) - source_parts.pop(0) - full_parts = ['..']*len(source_parts) + dest_parts - if not dest_is_directory: - full_parts.append(dest_filename) - if not full_parts: - # Special case for the current directory (otherwise it'd be '') - return './' - return os.path.sep.join(full_parts) - - - -############################################################ -## Bootstrap script creation: - -def create_bootstrap_script(extra_text, python_version=''): - """ - Creates a bootstrap script, which is like this script but with - extend_parser, adjust_options, and after_install hooks. - - This returns a string that (written to disk of course) can be used - as a bootstrap script with your own customizations. The script - will be the standard virtualenv.py script, with your extra text - added (your extra text should be Python code). - - If you include these functions, they will be called: - - ``extend_parser(optparse_parser)``: - You can add or remove options from the parser here. - - ``adjust_options(options, args)``: - You can change options here, or change the args (if you accept - different kinds of arguments, be sure you modify ``args`` so it is - only ``[DEST_DIR]``). - - ``after_install(options, home_dir)``: - - After everything is installed, this function is called. This - is probably the function you are most likely to use. An - example would be:: - - def after_install(options, home_dir): - subprocess.call([join(home_dir, 'bin', 'easy_install'), - 'MyPackage']) - subprocess.call([join(home_dir, 'bin', 'my-package-script'), - 'setup', home_dir]) - - This example immediately installs a package, and runs a setup - script from that package. - - If you provide something like ``python_version='2.4'`` then the - script will start with ``#!/usr/bin/env python2.4`` instead of - ``#!/usr/bin/env python``. You can use this when the script must - be run with a particular Python version. - """ - filename = __file__ - if filename.endswith('.pyc'): - filename = filename[:-1] - f = open(filename, 'rb') - content = f.read() - f.close() - py_exe = 'python%s' % python_version - content = (('#!/usr/bin/env %s\n' % py_exe) - + '## WARNING: This file is generated\n' - + content) - return content.replace('##EXT' 'END##', extra_text) - -import os, subprocess -def after_install(options, home_dir): - etc = join(home_dir, 'etc') - ## TODO: this should all come from distutils - ## like distutils.sysconfig.get_python_inc() - if sys.platform == 'win32': - lib_dir = join(home_dir, 'Lib') - bin_dir = join(home_dir, 'Scripts') - elif is_jython: - lib_dir = join(home_dir, 'Lib') - bin_dir = join(home_dir, 'bin') - else: - lib_dir = join(home_dir, 'lib', py_version) - bin_dir = join(home_dir, 'bin') - - if not os.path.exists(etc): - os.makedirs(etc) - subprocess.call([join(bin_dir, 'easy_install'), 'JSTools==0.2.2']) - - -##file site.py -SITE_PY = """ -eJzVPGtz2ziS3/krsHSlKGVkOo/ZqS1nPFdO4sx4z5N4J5na3HpSWkqCJI4pkkOQlrVXd7/9+gGA -AEn5sbP74VSpWCKARqPRbzQYhuFpWcp8ITbFosmkUDKp5mtRJvVaiWVRiXqdVovDMqnqHTydXycr -qURdCLVTMfaKg+Dp7/wET8WndaoMCvAtaepik9TpPMmynUg3ZVHVciEWTZXmK5HmaZ0mWfoP6FHk -sXj6+zEIznMBK89SWYkbWSmAq0SxFJe7el3kYtSUuObn8R+Tl+OJUPMqLWvoUGmcgSLrpA5yKReA -JvRsFJAyreWhKuU8XaZz23FbNNlClFkyl+Lvf+elUdcoClSxkdu1rKTIARmAKQFWiXjA17QS82Ih -YyFey3mCE/DzllgBQ5vgnikkY16IrMhXsKZczqVSSbUTo1lTEyBCWSwKwCkFDOo0y4JtUV2rMWwp -7ccWHomE2cNfDLMHrBPn73MO4PghD37O09sJwwbuQXD1mtmmksv0ViQIFn7KWzmf6mejdCkW6XIJ -NMjrMXYJGAElsnR2VNJ2fKt36LsjwspyZQJzSESZO3MjjYiD81okmQK2bUqkkSLM38pZmuRAjfwG -pgOIQNJgaJ5Fqmo7D61OFACgwn2sQUo2Sow2SZoDs/6YzAntv6b5otiqMVEAdkuJXxtVu+sfDRAA -ejsEmAS4WWY3mzxLr2W2GwMCnwD7Sqomq1EgFmkl53VRpVIRAEBtJ+QtID0RSSU1CZkzjdxOiP5E -kzTHjUUBQ4HHRiTJMl01FUmYWKbAucAV7z78JN6evT4/fa95zABjmV1tAGeAQhvt4AQTiKNGVUdZ -AQIdBxf4RySLBQrZCucHvNoOR/fudDCCtZdxd4yz4UB2vbl6GlhjDcqE5gpo3H/DkIlaA33+5579 -DoLTfVShhfO37boAmcyTjRTrhPkLOSP4VsP5Li7r9SvgBoVwaiCVws1BBFOEByRxaTYqcilKYLEs -zeU4AArNqK+/i8AK74v8kPa6wwkAoQpyaHSejWnGXMJC+7Beob4wnXe0Mt0lsPu8KSpSHMD/+Zx0 -UZbk14SjIobibzO5SvMcEUJeCKKDiCZW1ylw4iIWF9SL9ILpJCLWXtwTRaIBXkKmA56Ut8mmzOSE -xRd1691qhCaTtTB7nTHHQc+a1CvtWrvUQd57EX/ucB2hWa8rCcCbmSd0y6KYiBnobMKmTDYsXvW2 -IM4JBuSJBiFPUE8Yi9+BoqdKNRtpG5FXQLMQQwXLIsuKLZDsOAiEOMBOxij7zAmt0Ab/A1z8P5P1 -fB0EzkwWsAaFyO8DhUDAJMhcc7VGwuM2zcpdJZPmrCmKaiErmuphxD5ixB/YGdcavC9qbdR4ubjL -xSatUSXNtMlM2eLlUc368RWvG5YBllsRzUzXlk4bXF5WrpOZNC7JTC5REvQmvbLbDnMGA3OSLa7F -hq0MtAFZZMoWZFixoNJZ1pKcAIDBwpfkadlk1Ekhg4kEJtqUBH+ToEkvtLME7M1mOUCFxOZ7DvYH -cPsHiNF2nQJ95gABNAxqKdi+WVpX6CC0+ijwjb4Zz/MDp54vtW3iKZdJmmkrn+TBOT08qyoS37ks -cdREE0PBCvMaXbtVDnREMQ/DMAiMO7RT5mthv02nsyZFezedBnW1OwbuECjkAUMX72GhNB23LKti -g80WvY+gD0Av44jgQFySopDs43rM9Aop4Grl0nRF8+twpEBVElz+dPbu/PPZR3EirlqtNOmqpC8w -51meAGeSUge+6EzbqiPoiborRfUl3oGFpn0Fk0SjSQJlUjfAfoD6p6qhZljG3GsMzt6fvr44m/78 -8eyn6cfzT2eAIJgKGRzQktHCNeDzqRj4GxhroWJtIoPeCHrw+vSjfRBMUzX9lV3jExZ27QddHX/9 -RZyciOjX5CaJAvBF2q68Lz8SW37alRKG1vBnVKhxECzkElj4WiKjj56SfznmAUAX6Floe/drkeam -nZq9KUgORzQCcJhO51miFHaeTiOgFg0Y+MCAmJ1U5N4RDCx37tCxRgU/lQTq5jhkgv8NoJjMaByi -wSi6Q0wnYPvNPFGSe9HyYdx0irI/nY70hCAUxLbguLA4R8J0QdmvUvAPaftRF8xUkeFPhI/SRFKA -IQpqG9wkHYLEN0nWSDVyFgVEHI06ZESFlSpiCjD1I7Bo7daNx11qgsuDCGE3IF9WgDaqOpTDzwH4 -DSD2JhjCgIljGKYZYvpn9tgJB3DdIlSbSnWgsJYRl2eX4uWzF4foFkDstrDU8bqjpUvzRtqHS9it -lawdhHlUNCH+Hrt0WaK+wqfHd8PcxHZn+qyw1FtcyU1xIxeALTKws8viJ2qBCBfWMU9gF0E/kl1l -PWb8rwTjOV49SAvaYKDehqCY/Tdbf8BBtcwVaAMOUInUOnpmk1JWxU2KRnu2041gc0BjoeUxDkLg -bJzHZGhawA6BN5kjpbYyAp1UNez4Ed4IErX2otVuMYG7QHX5hb5e58U2n3JEeYKabzS2rIuCpZkX -O7RbcCDegS0AJAsIkFqiMRRwnQXK1iEgD8uH5QJlyUcHQGAwFYU9DiwTMtESOfrCaRHG+JUg4a0k -2t0bMwWFLIYYDiRqje0DoyUQEizOKjirGjSToayZbjCxQxKf6y5iDuV8AB0qxmC7RhoadzL0uzoG -5SwuXKXkjEOz+PnzZ2YbtaY8BSI2w0WjKV6SxYrLHVi3FHSC8Ww460FssAUnEcA0SrOmOPwoipK9 -GtjPSy3bYIwhSqrr8vjoaLvdxjpKL6rVkVoe/fFP33zzp2esExcL4h9YjiMtOmUVH1Ebeobxt8YC -fWd2rsOPae5zI8EaSfJuyKVD/L5v0kUhjg/HVn8iF7e2Ev83/gQokKmZlKkMtA1bjJ6owyfxSxWK -J2Lk9h2N2TnQwaa1YkaDQhuoJBhRF2COwXmYF01eR44iVeIrsG4Q6S7krFlFdnLPRpofsFSU05Hl -gcPnXxADnzMMXxlTPEUtQWyR5svCIf1PzDYJuShaQyB50UT1otDdsBYzxF08XN6tw+cIjVlhqpA7 -UCL8Lg8WQNu5Lzn40f4l2j3HvzQfzxAYSx8Y5tXe3QgFh3DBvZi4UudwNbqdIE1bVs2gYFzVCAoa -PLUZU1uDIxsZIUj0bkzQzBurewCdOhk4E2ebXYAe7jw9a9dlBccTQh44Ec/piQQ/9bjX9oy3tsky -Sox0eNSjCgP2NhrtdAF8OTIAJiKsfg65p96W8w+dTeE9GABWcC4FGWzZYyZscX3A8CAcYKee1d83 -mmk8BAI3ifo/DDhhfMITVAqEqRz5jLuPwy1tOX/UQXi/wSGeMrtEEq32yFZXdwzK1J12aZnmqHqd -PYrnWQFOsVWKxEdtu+8rUCyCj4dsmRZATYaWHE6nE3L2PPmLdD/MQq0ajNfddAZitEkVGTck0xr+ -A6+C0gSU0wFaEjQL5qFC5i/sXyBydr36yx72sIRGhnC77vNCegZDwzHtBwLJqJMaIAQ5kLAvi+Q5 -sjbIgMOcDfJkG5rlXuEmGLECMXMMCGkZwJ0avfgGn8R4kEACipBvayVL8ZUIYfu6kvow1f0v5VKT -CBg5HchT0BmEEze74GQWTjqZBp+h/RwDHTmUBXDwDDweN1/usrlhWpv4AF/d19sWKVDIlAsJxy6q -Xwxh3JzsH06cHi2xzCSGobyJvJMRM9M4sNutQcOGGzDennfn0o/dhAWOHUWFeiE3txD+RVWq5oWK -ML7tpS7cj+aKPm0sthfpLIQ/3gaE4y8eJJl10cG8xSKptmkekYrRKzzxiddDxy7Ws0JHHyneOQJU -MLV39K4CFqYzviNgeJRVCJtlpLRf3gd750pDC5eHh55fe3X88kt/+ZN9KRj7GSbm2W1dJQrpmTFZ -mW2Rnn0Li2oRFpfkO31Kp09x0Y+vCgVhnvjw8bNAQnACc5vsHrf0liURm3vX5H0M6qB57iVXZ3XE -LoAI6i1klKPo8Yz5cGQfu7g7FvYIII9imDs2xUDSfPLPwLlro2COw8Uux0RXV6jxA83ffD0dSF26 -SH7zdXjPLB1iDIn9qOOr2Znp9FwMLtsMqWSSkTfgDKK0X97yju1TjlnlUoCmmezLgFuIH9NulHoL -v9e9F9mZzwHRA+LgYvYrRJNKJ6BukjSjRDigcXiIes4EwhzbD+PjQbobZUwagU/xbDIYq6irZ7Ax -EUfe4/5ytOdyapKzAxGj+ZSJ6qNyoM+t22MX7yzaPXLbL/uDtvTfpLMeCchbTThAwAeuwRwJ/v9f -CSsrhqaV1bij9ZW8W88bYA9Qh3sckTvckP7UfIK0NM4Ey50ST1FAn4otnQNTsg2PDgDKgv2MATi4 -jfo08U1TVXwmSHJeyuoQD8kmAktgjKdBlTV9MEfvZY2Y2G5zSl46BRPFkOqMdDrSriRqPclhkV0X -Jokh85u0grGgVUbRDx9+PIv6DKCnwUHD4Nx9NFzycDuFcB/BtJEmTvSYMUyhxwz556Uq8ji0q1zN -Oa1JEWqy9QnbywyayHJ4D+7JEXgneHz4iTHbfC3n11NJB7rIpjjUyZK+wWbExJ7z+oU1KllSdRCs -ZJ41SCt29LCsa9nkc0qY1xLsua7BxJoMOqblhNAyS1ZiRIMXmIzQ3Ej5ipuk0t5OWRVY9SeadHG0 -ShdC/tYkGQZ6crkEXPA0QzfFPD3lJMRbPmnmajAl502V1jsgQaIKfRhEh9JOx9mOFzrykOS8PxMQ -j6mPxUdcNrYz4RaGXCZc9FPguEiMxHCAOa1D7qLn0J4XU5x1SsWTE0aqf1BLj4PuDAUACAEorD8c -61yO3yKpyT1xoj13iYpa0iOlG3sW5HEglNEYY1/+TT99RnR5aw+Wq/1Yru7GctXFcjWI5crHcnU3 -lq5I4MbaNIaRhKFURjfPPVgF4WYheJqzZL7mflhUh8VzAFGUJqAzMsW1pV6ugw98CAipbecEkh62 -VQ0pV+tVBSdFNUjkfjzV0MGjqQp2BlONhB7MSzE+277KDn/sURxTDc6MhrO8LZI6iT25WGXFDMTW -ojtpAUxEt8iDs2f5zXTG+b6OpQov/+vTDx/eY3cEFZrzbhqGm4iGBZcyeppUK9WXpjbYKIEdqadf -mUHDNMCDB+ZaeJYD/u8tHfkj44gtHVkXogQPgGptbDe3IiWKOs916Yp+zkzOpw8nIszrsF3UHiKd -Xl6+Pf10GlISKPzf0BUYQ1tfOlx8TA/boe+/ud0txXEMCLXOpbTGz12TR+uWI+63sQZsx+199qXz -4MVDDPZgWOqv8t9KKdgSIFSs04GPIdSDg5/fFSb06GMYsVeS5Z61sLNi2xzZc1wUR/SHEtHdCfzT -L4wxpkAA7UKNTGTQBlMdpW/N6x0UdYA+0Nf73SFYN/TqRjI+Re0iBhxAh7K22373z8vcs9FTsn59 -9v35+4vz15enn35wXEB05T58PHohzn78LKhgAA0Y+0QJnpXXWJoChsW9QSIWBfxrML2xaGpOSsKo -txcXOne/wTsEWFSKNieG51zXYqFxjoaznvahLkhBjDIdIDmXNah+gy5zYLy04YsCqtCFp3QHZIbO -aqNDL30Jx1zWoYPOGKQPOrukYBBccwRNVB5cm6iw4jMhfYFlAClto22lQEY5qN75sXMiYvLtXmKO -BsOTdrBW9FeRi2v0JVZllkIk9yqysqSHYb1Eyzj6oT3yZLyGNKAzHGbWHXnVe7FAq/Uq4rXp8eOW -0X5rAMOWwd7CunNJ9QJUGIvVTiLCTnxyEMlb+Gq3Xu+Bgg3Do58aN9EwXQqrTyC4FusUAgjgyTVY -X4wTAEJnJ/wE9LGTHZAFHtdHbzaLw79EmiB+719+GeheV9nh30QJUZDg2pJogJhu57cQ+MQyFmcf -3o0jRo5qNcVfGqy7BoeEsnyOtFNBC5+pTkdKZktdcODrA2zQfgI1d4ZXsqz08GHXOEIJeKJG5DU8 -UYZ+Edb/WNgTXMq4AxpLyi1meDXLPZg2nwPxcS2zTFchn7+9OAPfEavcUYL4nOcMpuN8CR6q6mos -vjrWAYVHrtBcIRtX6MLSsfsi9roNZmZR5Gi0d1Jv94myn/1RvVRnlaTKRXuEy2ZYTp13jNwM22F2 -lrm73w3p7HYjuqPkMGNMLyuqa/Q5AzianiYcGEHEhJX0JtnMp4tpXptCtiydgzYFxQtqdQKigiTG -62LEf0XO6d6iUuaWCTwsd1W6WteYUofBMVW4Y/cfTz9fnL+nkvEXL1vfe4BFJxQPTLi44AQrxzDn -AV/cajDkrel0iHN1E8JAHQR/uk1ctXDCE/TGcXoR/3Sb+JrPiRMP8gpATTVlV0gwDHCGDUlPKxGM -q42G8eNWhrWY+WAoI4m3CnQBgLu+Pj/anh2DQtkf0/iIs4plqWk4MoPdSqXuR69xWeLhymI03Ala -hyTMfGYw9LrXsq8myv30ZBFvHAJG/d7+HKZqqNdVL8dhtn3cQsGttrS/5E7G1Ok3z1GUgYgjd/DY -ZbJhVay7Mwd61bU9YOJbja6RxEGFHv6Sh9rP8DCxxO5FK2Yg3W4gU4D5DKnvZTTgSaFdAAVCRaEj -R3In46cvvDU6NuH+NWrdBRbyB1CEukSTSv+LCjgRvvzG7iM3EVqoSo9F5PgrucwLWz+En+0afcvn -/hoHZYBSmSh2VZKv5IhhTQzMr3xi70nEkrb1OOYq7VRLaO4GD/V2D4P3xWL49MRg1uGDXr9ruetq -I5862GHwgoAPoUq2oN3Lph7xXu09LMDu+gh2FGGS5LdoD73uQU/DQr/rt4EzHPwwsYx7ae1V5/JJ -ZBu0XzmvIGCqFR2WOFbYeIiuYW5t4ElrhUP7VFeM2N8DN3qcOlQXLqPgQvVWGOoOnVA/5LslfF0u -pdrl9uqDblvIG5kV4BZBxIWl6b/a0vRxPJjquAevFhUk6C/aHU/ya/IQ3/z1fCLevP8J/n8tP0BM -gdexJuJvgIB4U1QQW/GVQLqrjWXtNQdNRaPwzhZBozQ9X2tHZ+XSWwceCeh6e7/Q3uoHgTWG1Ybf -pQAo8hrpmmxrHU0VOfw211z6bphxkYZ2JdSNSIb9xf9YMH+ke8brepOhonSSBO12XoUX52/O3n88 -i+tb5CPzM3SSCH79C65IH5FWeBw0EfbJvMEnXxyP8QeZlQMOo465zEUCjLlEBG55aeMsvqqfWN86 -qTBwFuVuUcxj7AlcxXeX6i14kGMnvLrXwnnmBWGNxvoQqXVj8TFQQ/zSlfgQOtIYvSYaSQglM7xE -w4/jcNgGTQRlduHP0+vtwk0M69sQtMAupu2qR/5wq3TWTGcNz2UmQu3E7oS5I5elidrM5u7dqQ+5 -0C9bAHVCmX65TJqsFjKHqILCXLr1DlrVve7EcsLcwrqc7gBRoiLbJjvl1JokSoQ4a0gXd/FIgnJm -EIX+mFyz7sV7WKLhO5oAnRCl2KFwhqpmvmY55nBAq7ve0fs2zV++iHpE5kk5Rpy3ThysE10mxmgl -a71+fjAaXz1vzSjlZefeZcd5CRbG5ZQDUJ/l06dPQ/Ef91t+RiXOiuIaXBKAPRQQigtq3mOz9eLs -bvW9WtMSA0vO1/IKHnyh/LF93uSUnLtjKG2ItH8NjAj3JrL8aPp3bCCnrSo+auUefGSjbcfPeUqv -VMHkikSVq99Mg4kXI1DEkqAbokTN0zTiQB32Y1c0eE8JE22aX+QtcHyKYCbYimdEHGau0buikkXL -PRadExES4JBKiHg2uuhJN3UAz+nlTqM5Pc/Tuq2xf+YeH+o7yrV9U4rmK5FsUTLMOjrEcK68eaza -epfFnSzqeevF/MpNuXVWyc334Q6sDZJWLJcGU3hoNmleyGpujCruWDpPaweM6YdweDC9IIYMUBwM -oBSChifDsLASbVv/YPfFxfQDnaQempl0AU1tX7rD6ZEk79SRxXE7PyViLCEt35ovY5jlPSV2tT/g -zSX+oNOKWGDtvRvAverV5PrOP1cwtC8CADj0nhmrIC07ejrCebmRhc9Mqx359hUBTj04hqeE201a -1U2STfW99Cm6bFN7tKzxtFeE7rz8Zn0WcKgLcDUPdbE0+A6mzgTpibWOplwd4nMdnsfutRv/hkpZ -oK/3wtPjmPR9xpfgHQ2OPb8yFzceovLN9YFe5b2L5YSqeqJxt1ax1wtPECJd80Vp2SEP+1FTGliu -K/xQABkAgD/s+EVfdU6BnNI0rhvdl/rvAf3m67vAukpmsGiW8u2+4tEXl9wq1jbhz7JsfL41uJUo -GQtz1VQLHt/KQylhlW9vEptah+6FCGh++JLvWPADTtMinOzwiYq0m2048i5aWfzuIlXbKfinqKRH -DdMK3TwsM1wn3ILi2pTHNhgybxLAFO3ILT7BT309WJad4MtqkKCH9XV01/J5/F1r1z0Cu3Jz9tJb -u3/9wqWBHrufX4ZowC6oJsSDKjotRtN/jehO9LHgcHpDf5b2tXmc5SAe1KhNNEtukrn7HQ+nD/mt -e219oHM5wt31zpr2Xhs27Nzn5D4380EcPrf33+h0daHZiw0WvYNlyvU6U7laqWmCr/CZkpdDZ8s9 -82Xs5jt6fYtM1M6YO7xRDyAMq+gqILfQD3YdPCl+lSAfzTpXpwVNTQVMTkWUShccvWrbCuBijlpp -vEmKcElTmEnMN6imKitwR0L9wjk+Mxwqs2qBmghqk6hrg7oZMdHvH8Mp+KDaXL/hWJldHI86QAiu -ynfe28E1gtOpbQN+edZeBEwnliFk3mwgPq7bO/D+2UQqvnNmoEtXuMFOjNSKXYdTXMRSyx8OUhil -2O9fafPveTd33P4bW5X2cLaiETr8fszFQkfKDTent/YdOO67Fxb0HkOKiPjdCcJ2a7nP3vuHrTAv -dCFFqIMWbtUvmeAXinFWBSuyHD4CuXevPPiVcVZnscNg0XCeuYqh/1YBvDVHhnboZUE9Lui/Fshn -hnZ+X29YZullovd0tlQ84R6Diqedbdy68ljEco8r7xcqPtKV9+A/0JXXr3YCa6Lx0fpgsHTxHp+f -1YT7nqSWEWDMFIiEyfbOW3aMPRy5hYDgkKe3oX17IOtM53aBMRPIkf0XaBAIfh+ScqumvPeVmHmH -fG1fuujx9xcfXp9eEC2ml6dv/vP0ezoixrxVx2Y9ONbJi0Om9qFXkubGPfpYb2jyFtuBd4lxXbWG -0GvvHYkMQBiuoR/a0K4ic5v3DejVIvcHAeJ3L7sDdZ/KHoTcc7503at7mNepHQv0Uy70Mb+ccxnz -yGRNWRzalKhpb7NYWkZ7Qf6+jXNKbvrqRDul+lVVexIQY1v4RTuAySvkL5u7MlW8NkPCjkr3nc5U -rYY3IMw9b5DCuXReN0RvGmJQtf/y6AqUXYI5eHYYJ/ZFjNSP83TKvmEU8/BzGRuCeFcQwv76XGFf -yGwPFYKAFZ5+mQ4jYvSfzmzb06AnSlwd0mWnQ1Q2X+wv3DPt5P41xTOf2r6VQpnjUsx3Q+dlk7nn -OHZMbwA5f5QWLJZOdS1oviOgcyueCtgbfSZWiLOdiCBK1IcVWLBDdNRvlHGQR7vpYG9o9Uwc7rsK -414FEeL5/o6Lzm0TPeIFj1D3jFCNuXDgWGCsGdl3x0V8R5A5ryzoNRSe84HnGfrlh/D15ur5sU1K -Ir9js/uSA6R96Bj2q7aq/M4XHzmjiVeqCdUOYKHKuAv+S+iw5lLsD3B6NbJ7giBz4MSQQq99+Fzd -jPBeshp2EbV8dwwLEqMnakyLcqqKNe72ybi32FZl9WFwgfT9MHraD0AhlGHfBD/8rg1Qz890PDhr -6G1x1uHEa4WOPNAhuc8LPMJ4fS123eF0relBw6lc3BaZc4cu7+n9BrFmr4F7eYmO/bagu/KWB/bY -fr4gNjz++QPG98sp7PAXdznUttfLwUsJ7MRiAQ4ez3YoZB7HYF1AYY5ITWPtppFwvPjdktHhpnZp -yBXo8FFND74JkgILcmKn2vJbYxD8H2/QG9E= -""".decode("base64").decode("zlib") - -##file ez_setup.py -EZ_SETUP_PY = """ -eJzNWmuP28YV/a5fwShYSIJlLt8PGXKRJi5gIEiDPAoU9lY7zxVrilRJyhu1yH/vmeFDJLVU2iIf -ysDZXXJ45z7PuXekL784nqt9ns3m8/kf87wqq4IcjVJUp2OV52lpJFlZkTQlVYJFs/fSOOcn45lk -lVHlxqkUw7XqaWEcCftEnsSirB+ax/Pa+PuprLCApScujGqflDOZpEK9Uu0hhByEwZNCsCovzsZz -Uu2NpFobJOMG4Vy/oDZUa6v8aOSy3qmVv9nMZgYuWeQHQ/xzp+8byeGYF5XScnfRUq8b3lquriwr -xD9OUMcgRnkULJEJMz6LooQT1N6XV9fqd6zi+XOW5oTPDklR5MXayAvtHZIZJK1EkZFKdIsulq71 -pgyreG6UuUHPRnk6HtNzkj3NlLHkeCzyY5Go1/OjCoL2w+Pj2ILHR3M2+0m5SfuV6Y2VRGEUJ/xe -KlNYkRy1eU1UtZbHp4LwfhxNlQyzxnnluZx98+5PX/387U+7v7z74cf3f/7O2BpzywyYbc+7Rz// -8K3yq3q0r6rj5v7+eD4mZp1cZl483TdJUd7flff4r9vtfm7cqV3Mxr8fNu7DbHbg/o6TikDgv3TE -Fpc3XmNzar8+nh3TNcXT02JjLKLIcRiRsWU7vsUjL6JxHNBQOj4LRMDIYn1DitdKoWFMIuJZrvB8 -y5GURr4QrrRjzw5dn9EJKc5QFz/ww9CPeUQCHknmeVZokZhboRM6PI5vS+l08WAAibgdxNyhIghs -SVyHBMJ3hCcjZ8oid6gLpa7NLMlCN45J4PphHIc+IzyWPrECO7oppdPFjUjEcJcHgnHHcbxQ2mEs -Q06CIJaETUjxhroEjuX5xPEE94QtKAtDKSw3JsQTgQyFf1PKxS+MOsSOfOgRccKkpA63oY/lUpfa -zHtZChvlC3WlQ33fjXmAuIYy9AgPY9uBIBJb0YRFbJwvsIcLDk8GIXe4I6WwPcuK3cCTDvEmIs1s -a6gMgzscQn3uEsvxA88PEB9mu5FlkdCKrdtiOm38kONFxCimkRWGDvNj4rsk8lyX+JxPeqYW47di -uPACwiL4Mg5ZFPt+6AhfRD7SUdCIhbfFBJ02kUAlESGtAA5ymAg824M0B0bC4RPRBqgMfeNQIghq -2HY53kcZOZEIKfGpT6ARF7fFXCLFAzeWMbUgzGOe48Wh5XpcMEcwizmTkbKHvgk8FnvSpTIkIbLQ -FSxyhUUdhDv0YurcFtP5hkoSO7ZlUY4wcdQEJAnOXQQ+8KwomBAzwhlpWYFHZUCIQ0NuQS141kNi -W5EdMmcqUCOcCezAjh0hmOtLLxSImh0wHhDbgVQnnJIywhlpRwAogC+XSBXi+DGLIUXaPKRhJCfQ -io1wRliCh14QOSyOIyppCE9HFrLXQsxDeyrY7jBIhAppB5JzGOb7vu1Fns1C4BePozjwp6SM0Ipa -NLZdmzBCXceCM4BzofQ85gMoQlvelNJZhCSR2DPgnqTSRUVRGXsBs+AqoJ6YShhvaFGk0BrA7zqM -05iFDmXSA3w5gXQiIqfQyh9aJEQseWRBHRQkMla6ApjuhwAMHtnBVKT9oUVEAqu4BKvYoWULAeeG -ICefMhAeCaZQxh/FKOKuDAAIHmOERKHtIXG4G1LGuMt9PiElGFqEgonA8pFtB2CiKPJCByLAmL4X -o7SngDMYsRvzAyL9kMK/6B5QDYEFQzzPRYH5ZAobgqFF1JERCX0HZA/YpS5I2kKoufAlWgnfnZAS -juDOQoxkTDhzSWD7wrdtH2WIliICBE7mSzhiAhLJ2PfAAhxYbkkahEza0kEY8MiZqoBwaJEHjiXA -W4mWAQXouZ5t25KLyLXxL5zSJRp1Q5bqhZwYHok5+EOlIAA8ci3VWFm3pXQWMUrcCNiAnsOLXGap -nEW2wdkMzDJJA9HQIjt07BAgh0DHnNm+5ccW8SPqCtR57E9FOh5aBN2ZZ6GZsZWHqRcHwmOSCiuC -rcyainQ8QgYkGRo7cKsbRTwAOhEhrADgxQLXm+rvGimdRVIgtK7wiR1S22EIE/M9m4bgXjC/mGKS -eMhHjKBsbKlQkziCA5js2AWzhdSPHfQ4kPLrrDcRYLwpZ1Vx3tQD156U+zSh7byF3n0mfmECo8Z7 -feedGomatXjYXzfjQhq7zyRN0O2LHW4todMuwzy4NtQAsNpoAxJptPfVzNiOB/VDdfEEs0WFcUGJ -0C+ae/FLfRfzXbsMcpqVX2w7KR9a0Q8XeerC3IVp8O1bNZ2UFRcF5rrlYIW65sqkxoJmPrzDFEYw -hvEvDGP5fV6WCU174x9GOvx9+MNqfiXsrjNz8Gg1+EvpI35JqqVT3y8Q3CLT7qodOhoO9aJmvNqO -hrl1p9aOklJsewPdGpPiDqPqNi9NdirwW51M3QtcpOS8tf1ZEySMjV+dqvwAPzBMl2eMohm/78zu -nRSouf5APiGWGJ4/w1VEOQjOU6YdSbWvx/nHRulHo9znp5SraZbUvu5Layfz7HSgojCqPakMDMKd -YC1LTcCZ8q4hMfV2Sp0yrl8RxuPAEY+GGmmXz/uE7dvdBbRWRxO1PGNxv1iZULL20qPaUsnpHWPs -RTE4IHlOMHPTSyYIvkZG1gmuVc5y+CMtBOHni/rY473sqafdrrdrzia0mKrRUkujQqvSOESfWLA8 -42Xtm1aNI0GiKKfCI6qskipB6LKn3nlGHfHG/jwT+jyhPhvhtV5wap4qH754PqK0bA4bRCNMn+UU -+Qk7iVqVus6IcRBlSZ5EfcBxKbrHR50vBUlKYfx4LitxePeL8ldWByIzSIV79ckGoQpalPEqBZUx -9amH2Wao/vlMyl2NQrB/ayyOn552hSjzU8FEuVAIo7Y/5PyUilKdkvQAdPy4rglUHUceNG5bri5I -olJueymaXl02HhuVYFt261GhXTCgLRITnhVFtbTWapMeyDVA3e30pn+6Q9tjvl0TmJ0G5q2SUQcI -wD6WNXCQfvgCwncvtYDUd0jz6HqHgWizSa7l/KLx2+38VeOq1ZtGdl+FoYC/1Cu/zjOZJqyCazZ9 -9O9H/r9F+/lP+0v2T+T78u32rlx1tdzWsD7K/JgNAX/OSLaoVEl1JQLMUMd3ukaa4zpVLacsQyqb -xvepQIa0y6/kqRpSpQwAErCl1VAmRQlHnEpVDgtIOLehN17/3FN+YY7kfcw+ZsuvT0UBaYDzWsBd -MeKtFVjrksvCJMVT+cF6uM1ZOn5pKYYxQKIPw7nuV9qHUZ0+qFe+hLUayfNPA1Ev5eB01nyToCQS -elIM/l1e/SkHL9zO55ppXyrr35tuVfGjPAc8+80LpKrLmFxIwUhzVrckGj5rG5KqPiHWLcb/KcnW -EK0+A2hJ9rc4Vt1Tu14TbI37jxfOnODFvGbDlgwVqbDqRNKLEQ3JDImk/YihANdQB9m6RwqldZ61 -/erW6IHZ67sSvfddqVrveb9wRkfgda5Cbp87lM+MV8MWsSSfBbTfoiWvSeHveZItWwppl9biyoIp -cbpP/g5s3rbWCqra11GkZVUua7GrjSqwrz7niUqgoyCKL1t1yq4+BniuLp2KHIKUN8rWS2n+NFil -mnEVl+G76sJK85kU2VL5+fXvd9WfkDTA2iB5+VKW3+mUUJ+cLMVnkak/YM4Rys72Ij2qvu99nW29 -3qNLFTQnKv/VZztL5YoZKGFtAF1m6tYB5ZwJOBKvoA5V5wuEFs8KjwnG2bLUb/c5QCO4OWu2BHQ3 -Pc5lR6jM22w2Z7MlQExslIe1mANhe9Vu8VzUxLRHeKFE9ZwXn5pN18axZpecVqT5XE4hhUaJu3I2 -UygCDzDdtesFkHypxKZyCtGwVd8Ac/V7RhFJsb5KmR7oXjVUOsvWqpquXkNHoZO1StRk2TROqRDH -N/WP5aj3GmZnC8OaF8u53mLEe7rkGnww8TM/imx5texL4wc0/ffPRVIBfBBj+Fe328DwT2v10eCz -ip5qF1ihyhDQyPKiOOnkSMVImI57Pz1UF14Jvb7FxPZqPmabGsJhgKkGkuVqqHGNItqaGivW82c6 -hzvxwNR21GN49xKGQTUUbsYQgA02eheW5qVYrq4goqw2Wmj/ecNmLWhBwVT90sLW7D+5FH8fkOlL -NCyf11OMfeHc97c+NNUc+w6tVbOqJYiXmunRh9G3Oul6eOiw+kriZc3tAUNP6tZ1SzYcIwZThI6Z -Ko3e7MDywwGGmoMesj3OIc1A1l5NjLSLU3CB9vPqlTpteVjpNH0Wi0KntTAUjf9mqihLlZ9HXKXU -vuYQLDplmAA/LTuzhg1n0m/czd2u8dZuZ2wxElqmZdqL/3pE+CsAXoOrmotpmacCtToxGrdNP8ik -buyvGvpCHPLPGm91JOrvPOgJGMxRAXrT38DdUac+2ZI3RfWPYbPSm7z63c71MPgfDHT4eaP/Hk1t -m+ls/59T8laZdYJ/U8pVNr9Ud225PQxndu1sa4XEh1WK/RE4pjNFPXk5Q9Uuv5MDOvW15jemsDrN -5z9etUXzdYsoc4DgkyaiQh3/IgnRJF0Sev6CvMXyB7RT8/bbOebxPJw+5/X3bq6/mmKuFs2x5rHj -p3aEKS/w/LN+aqgSoackrV7X58QQ+aSGu7NC5H4WF838o3qt9ly5E3txiO65L921+lOtWF66ai2k -5UJNmouCLi7PumNm9e5Dc0QtW1J98ZhadmRXj4A1RX+Yqz/uig3+rYEVGB+aTrNuyNqNTJDvoVyu -HrqXzRIWd9R5VEPFfF5PCjVJ9x2DCGCErNqJQX+faNveNZ9EVRetur/sT+c73THsdk3Wdy5pZKwN -7ZY3TUvUOuDN2NgDqTANbqGnWQpSsP1y/jHrfx/oY7b88LdfH16tfp3r9mTVH2P02z0segGxQeT6 -G1mpIRQKfDG/LtIWEWtV8f8PGy3Y1K330l49YAzTjnyln9YPMbri0ebhZfMXz01OyKY96lTvOWAG -M1o/breL3U4V7G636D4FSZVEqKlr+K2j6bD9+4P9gHdev4az6lLp0VevdrrlzubhJV7UGHGRqRbV -178BYnMUkw== -""".decode("base64").decode("zlib") - -##file distribute_setup.py -DISTRIBUTE_SETUP_PY = """ -eJztG2tz2zbyO38FTh4PqYSm7bT3GM+pc2nj9DzNJZnYaT8kGRoiIYk1X+XDsvrrb3cBkCAJyUnb -u5mbOd3VoYjFYrHvXUBHfyp3zabIndls9m1RNHVT8ZLFCfybLNtGsCSvG56mvEkAyLlasV3Rsi3P -G9YUrK0Fq0XTlk1RpDXA4mjFSh7d8bVwazkYlDuf/dzWDQBEaRsL1myS2lklKaKHL4CEZwJWrUTU -FNWObZNmw5LGZzyPGY9jmoALImxTlKxYyZU0/osLx2HwWVVFZlAf0jhLsrKoGqQ27Kkl+OErbz7Z -YSV+aYEsxlldiihZJRG7F1UNzEAa+qk+PgNUXGzztOCxkyVVVVQ+KyriEs8ZTxtR5Rx4qoH6Hfu0 -aARQccHqgi13rG7LMt0l+drBTfOyrIqySnB6UaIwiB+3t+Md3N4GjnOD7CL+RrQwYhSsauG5xq1E -VVLS9pR0icpyXfHYlGeASuEo5hW1fqp33WOTZEI/r/KMN9GmGxJZiRR033lFXzsJtU2CKiNH02Lt -OE21u+ilWCeofXL4/fXlu/D66ubSEQ+RANKv6P0lslhO6SDYgr0ucmFg02S3S2BhJOpaqkosViyU -yh9GWew94dW6nssp+MGvgMyD7QbiQURtw5ep8OfsKQ11cBXwq8oN9EEEHPUIG1ss2Jmzl+gjUHRg -PogGpBizFUhBEsSeBV/9oUQesV/aogFlwtdtJvIGWL+C5XPQxR4MXiGmEswdiMmQfBdgvnrm9ktq -shChwG3Oh2MKjwv/A+OG8emwwTZ3dlzPXHaMgBM4BTMeUpv+0FNArIMHtWL9aSydog7qkoPVefD0 -Nvzp+dWNz0ZMY09Mmb24fPn8/aub8MfLd9dXb17DerOz4C/B+dmsG3r/7hW+3jRNeXF6Wu7KJJCi -CopqfaqcYH1ag6OKxGl82vul05lzfXnz/u3NmzevrsOXz3+4fDFaKDo/nzkm0Nsfvg+vXr98g+Oz -2UfnX6LhMW/4yY/SHV2w8+DMeQ1+9MIwYacbPa6d6zbLOFgFe4CP888iEyclUEjfnectUF6Zzyci -40kq37xKIpHXCvSFkA6E8OILIAgkuG9HjuOQGitf44EnWMK/c20D4gFiTkTKSe5dDtNgk5XgImHL -2psE2V2Mz+CpcRzcRrDlVe65lz0S0IHj2vXVZAlYpHG4jQERiH8tmmgbKwydlyAosN0NzPHMqQTF -iQjpwoKiFHm3iw4mVPtQWxxMDqK0qAWGl94g14UiFjfdBYIOAPyJ3DoQVfJmE/wM8IowH1+moE0G -rR/OPs2nG5FY+oGeYa+LLdsW1Z3JMQ1tUKmEhmFoiuOqG2QvOt1256Y7yYtm4MBcHbFhOVchd0ce -pF/gGnQUQj/g34LLYtuqgMe4rbSumMlJYCw8wiIEQQv0vCwDFw1az/iyuBd60irJAY9NFaTmzLUS -L9sEXoj12oP/fK2s8FCEyLr/6/T/gE6TDCkW5gykaEH0bQdhKDbC9oKQ8u45tU/HT37Bv0v0/ag2 -9OoEv8GfykD0mWoodyCjmtauStRt2gyVB5aSwMoGNcfFAyxd03C/SsUTSFGv3lBq4rnfFW0a0yzi -lLSd9RptRVlBDESrHNZT6bDfZbXhktdCb8x4HYuU79SqyMqxGih4tw+TJ8f1Sbk7jgP4P/LOmkjA -55j1VGBQV18g4qwK0CHLy/NP889njzILILjbi5Fx79n/PlpHnz1c6vXqEYdDgJSzIfngD0XVeGc+ -6+Wvst9h3WMk+Utd9ekAHVL6vSDTkPIe1Rhqx4tRijTiwMJIk6zckDtYoIq3lYUJi/M/+yCccMXv -xOKmakXnXTNOJl63UJhtKXkmHeXLukjRUJEXTr+EoWkAgv96Jve2vA4llwR6U7e8W4dgUpS11ZTE -In+zIm5TUWOl9LHbjdtzZQw49cSDL4ZoBusNAaRybnjNm6byBoBgKGFsBF1rEo6zFQftWTgNDSvg -MYhyDn3t0kHsK2u6mTL3/j3eYj/zBswIVJnuzXqWfLOYPVWrzS1kjXcxxKfS5u+KfJUmUTNcWoCW -yNohIm/izcGfjAVnatWU9zgdQh1kJMG2gkLXm0DMbsiz07Zis+dg9Ga8bxbHULBArY+C5veQrlMl -8zGfTfFhKyXiudtgvalMHTBvN9gmoP6KagvAU9XmGF0C9jYVIB4rPt064CwrKiQ1whRNE7pKqrrx -wTQBjXW6C4h32uWwk/fGvtzAAv8x/5h737VVBaukO4mYHVdzQD7w/yLAKg4zh6kqS6EljfdsOCbS -2mIfoIFsZHKGfX8Y+YlPOAUjMzV2irt9xeyXWMNnxZB9FmPV6y6bgVVfF83Los3j3220j5JpI3GS -6hxyV2FUCd6IsbcKcXNkgV0WheHqQJT+vTGLPpbApeKV8sJQD7/oW3yduVJc7RqJYHtpEVHpQm1O -xfikkZ27HCp5mRTeKtpvWb2hzGyJ7ch7niYD7Nry8jZbigosmpMpd16BcGH7j5Je6ph0fUjQApoi -2O2AH7cMexwe+Ihoo1cXeSzDJvZoOXNP3XnAbiVPbnHFQe4P/kVUQqeQXb9LryLiQO6RONhNV3ug -DmtU5DH1OkuOgX4pVuhusK0ZNS1P+44r7a/BSqoJtBj+IwnDIBaRUNsKquAlRSGBbW7Vb65SLKsc -wxqtsdJA8cw2t1n/GqI6YOtnkBwHWIatf0UHqKQvm9rVIFdFQbKnHRaZ//F7ASzdk4JrUJVdVhGi -g32p1qphraO8WaKdXyDPn98XCWp1iZYbd+T0Gc4kpHfFS2c95OPrmY9bGrpsSZTikjcZPmLvBI9P -KbYyDDCQnAHpbAkmd+djh32LSojRULoW0OSoqCpwF2R9I2SwW9JqbS8JnnU0guC1CusPNuUwQagi -0AcejzIqyUYiWjLLZ7PtcjYBUmkBIuvHJj5TSQLWsqQYQIAu0UfwgN8S7mBRE77vnJKEYS8pWYKS -sS4FS2z6h8gzD4d9YCNwJm96V/gT2TyP7tqSuLiSCYfIGc0Fj6cNlbQIZB4qHJpTiHhuchP2MIVd -6KX7vR2B7HHaTi4lYkut/3wIYbaRFAtecsgPRr2ZtwiNKVKgJ0CURZsJiUlEsYxz5iYgad+6Niei -xK15Z4+QK5t8sDDSssBTNM0PqzS0TMdMNZinUEEYriEqLYsHb9XmEUYphYOGzXFqm/vsyZO77fxA -tSMPdfq6U03XDu+FjhjX8v3QIGDN+6SQjb7JIYj+lLwe1k9jnEFYpFjiTd93yB+Z38EBFvscpUYw -TpLRrx+rlfppUtv281HJUEtlwP5HPYVaZsq7w1u1MtKaMNshTeUzdcdx/mF+I9WamJEkNhdbHQTx -LQQ0N3jz6kVwXOPpER5EBvhn0kR9h+hkHEGfXcj2nTQOjVP1U7GMxK+ebVRRr186mtisuIe8FDgV -ms1or0x5JDawd6GbwqOImdTY1puCDal/n99BzBn0uSHHUXsw5u53WStM8Tu1km8qps/ejZ6rnRSg -Wh3sBupfD+f6ZuvjCTbnTjAPH7ch9OIDU8DPEvzOncmW1bAS6TnQNyMpWzbPp811RwxwJloAckIt -EKmQp59F22B+iQFpy3e9G9clxTg3MtjjE/u6SDSSqJpvcKK3bRUtgexwACuj36AKnUySIVbN8Jnl -aFA1kRVHJ6becwNMgY+jns+G1FiV6Qgwb1kqGrdmqPhdPB/zs1M0xW/UNc/slvmjPpvqluOhPz4a -3NMYDslDwQxOnsYtXQUyKixNbzPBMu0L2PQSfK3skQNbNbGKE3s61u51f2cmNipyd7QTS4jnK0g7 -u6NUnKx2ZCQ0CNLd7Ojau52C94zDtB4w4OkRpA1ZBm44LJY/e/3BXKB7wiWUTlCfyEznsWp84Jks -Lv5L5g+cp0k7KJelAnnMoVrEpjmlq/GpMyG27e6JYWA8KuZ4n33UIMuofqPkfRemC1UnHXXv0WCB -jwPt8fadr/uSti9wXyNSJp5M83Lqyqw+RIIf8CBjb/wdyl/G5MmsPl/uXN3hnNnqCAlgf/4sWdVs -tCT2s8qQUQAT3HF6MdqKQjneinr92FYGZBjtpbG8Ht+fUZp1wabPpY6UCwfPH92h4BP8ZiuV9qqT -LGYuv//+BBmOrhuYL5+/QJ2SSdFyML7t88WfG88Mn9rHtD11GxCf3XV8G746yIr5I4b4KOf+KxZg -sMIML7K71sWXSWz5Vnbf9gYXy3mSwkwtxrCsxCp58LSr7b17F3LIN6ujNKhs7o1TaoNc/K6ugWnA -D/oBYlYsHowg9vT84lOXkNCgry+LibzNRMXlNTKzpkRQec9Spi4nJxXsVZ7ey02Mc13YBOAIYM2q -qbE5inq5QD8u8VgK1qYoVbuRZpZp0ngurrNw5x9ORmdKBgs0+8zFFK7xwYakCut7SYX1mDAFZZN3 -376R/LEfFg7IrT8Q5FMLlb+ZUsVwvHV4ctLWonKpM97f7VQnXdiFnJJ4YMkOw17Fn+jtWPOvI05n -YsbRmb7hZ7PNvWe7hxoBR2wrXDCvCEiwhFwjawTtNC6mxIWQjKmFyLBVbp7wTRta9HWLtjNMwdXV -GWTDdENGDMKcESZv6wBzqOGxdPBOHlliEgterwJnM0j77QnxSI4UgRHDgty08qiKcze7Ukz4hn0d -4yzk+durP5jweV9cjRGCUg4V0ryQZF6PN1N9WfDaRXPEYtEIdfELgzMeJncRDjU1HmeU3UnSYkxe -oIfG+mxe2ze6C3Jp0G7dZrCsonhBfXHpGFEhyTEmD0RsWUG5HYtY3uBPVgre/K1AbRT1sbozlvl9 -X143h838fxhFbJTZpaCwAUP9McGASLbzbVcZp9oqLzUDLRuoBvZXDIM0C6xSyrE2b5ypLVk2EYg8 -VhGErj3t2VR+Ii+k9cIb0IH2vb8/ZZWqnqxIAxy21qOlWWHcWdxP0r6MyELK4QRJkejtyy9R54ZV -/hfkmHuTzAPnBCPeDOdNTwpM3ehOn9Cs6YhUuj86rjT8fS7Goh1m979XniN66cAuF8bZRsrbPNr0 -+Vz/Zhwp36mRwZ4xtLENx5YR/qhGQlD5rX+UgVD6Zv/wZv4n9rTL8qTj0/c4rD+66Eg0Lq/WIl3J -ru9iFsx8lgk8YK4X6Lj7kyp14ZYODBWEPLagw+IKtiTpx6+RvIqi75tqvvYH3+j48DdBxTbHQjIr -Yvz1kHSy2KkmgFJUWVLX9HOe/iBBI0lA0tTwAcbGdcBucQNud4EAf8oDSFeCCJlctwVCFQfgESar -Hbno7mSmxVMiIsOfZtGlAuAnkUzdK40HG8RKVUAtlju2Fo3C5c2HJ+0q64mKcmd+h2oGcmx1c0wy -VF471gCK8f31MpMDoA+fuuCrxTIJunoAA2C6crp8H1YipwNuW4EMyk81rJq3I+M/0oQN6FEXH2q+ -EihVMTr+7SEDXkIZF3tqjaG/0HQtiFsB/jkIiPeOsFXx9dd/owQhSjIQH5UpQN/ZX8/OjIwnXQVK -9BqnVP4ucL8T2KMSrEbumyR3Sc6ojcX+zrxnPvva4BDaGM4XlQcYzn3E82xu8zAsykqCCbDSloBB -f7QyZhsi9SRmO0AlqfdsffMJojuxW2gFDPAeJagv0uwiAe7cZwqbvGKqGQTpEV0IAFydBXdWi6pL -4sB8acy8kdIZ4wMi6RDL2hvQAh8yaHIOSFKONkBcL2OFdz4FbOlw7DMAow3s7ACgysJNi/0NtyOl -iuLkFLifQt15bino8ObpqEq0XdQjZGG8XHughDPlWvAXT3gxRuhwkPGEqtx7n+25DNYHgqtDP4sk -Fbjk9U5Baed3+Jq4CqTjH0EBcQmdp2OGElLpG4ZIahiq39wR3V2T4/zi09z5N4dES24= -""".decode("base64").decode("zlib") - -##file activate.sh -ACTIVATE_SH = """ -eJytVFFv2jAQfs+vuIU+QDWK+tqKB6oigdRC1bBOW1sZk1yIpWAj2yGj0/77ziFAUijStPIA2Hc+ -f/7u+64Bk0QYiEWKsMiMhRlCZjCCXNgEfKMyHSLMhOzw0IoVt+jDeazVAmbcJOdeA9Yqg5BLqSzo -TIKwEAmNoU3Xnhfh9hQ0W/DbA/o0QKNBCyqNAOVKaCUXKC2suBZ8lqIpskQMz9CW4J+x8d0texo+ -Tr717thDbzLw4RWuwSYoi0z3cdvdY6m7DPy1VNoWibu9TDocB4eKeCxOwvgxGYxHg/F9/xiYXfAA -0v7YAbBd6CS8ehaBLCktmmgSlRGpEVqiv+gPcBnBm0m+Qp6IMIGErxA4/VAoVIuFC9uE26L1ZSkS -QMjTlCRgFcwJAXWU/sVKu8WSk0bKo+YC4DvJRGW2DFsh52WZWqIjCM4cuRAmXM7RQE5645H7WoPT -Dl1LulgScozeUX/TC6jpbbVZ/QwG7Kn/GAzHoyPkF09r6xo9HzUxuDzWveDyoG2UeNCv4PJko8rw -FsImZRvtj572wL4QLgLSBV8qGaGxOnOewXfYGhBgGsM24cu729sutDXb9uo/HvlzExdaY0rdrxmt -Ys/63Z5Xgdr1GassGfO9koTqe7wDHxGNGw+Wi0p2h7Gb4YiNevd9xq7KtKpFd7j3inds0Q5FrBN7 -LtIUYi5St1/NMi7LKdZpDhdLuwZ6FwkTmhsTUMaMR2SNdc7XLaoXFrahqQdTqtUs6Myu4YoUu6vb -guspCFm4ytsL6sNB8IFtu7UjFWlUnO00s7nhDWqssdth0Lu567OHx/H9w+TkjYWKd8ItyvlTAo+S -LxBeanVf/GmhP+rsoR8a4EwpeEpTgRgin0OPdiQZdy7CctYrLcq5XR5BhMTa5VWnk+f5xRtasvrq -gsZBx6jY5lxjh7sqnbrvnisQp1T6KNiX6fQV9m/D1GC9SvPEQ1v7g+WIrxjaMf9Js/QT5uh/ztB/ -n5/b2Uk0/AXm/2MV -""".decode("base64").decode("zlib") - -##file activate.bat -ACTIVATE_BAT = """ -eJyFUssKgzAQvAfyD3swYH+hItSiVKlGsalQKOyhauvFHOr/U+MzFcWc9jEzO7vkVLw+EmRZUvIt -GsiCVNydED2e2YhahkgJJVUJtWwgL8qqLnJI0jhKBJiUQPsUv6/YRmJcKDkMlBGOcehOmptctgJj -e2IP4cfcjyNvFOwVp/JSdWqMygq+MthmkwHNojmfhjuRh3iAGffncsPYhpl2mm5sbY+9QzjC7ylt -sFy6LTEL3rKRcLsGicrXV++4HVz1jzN4Vta+BnsingM+nMLSiB53KfkBsnmnEA== -""".decode("base64").decode("zlib") - -##file deactivate.bat -DEACTIVATE_BAT = """ -eJxzSE3OyFfIT0vj4spMU0hJTcvMS01RiPf3cYkP8wwKCXX0iQ8I8vcNCFHQ4FIAguLUEgWIgK0q -FlWqXJpcICVYpGzx2BAZ4uHv5+Hv6wq1BWINXBTdKriEKkI1DhW2QAfhttcxxANiFZCBbglQSJUL -i2dASrm4rFz9XLgAwJNbyQ== -""".decode("base64").decode("zlib") - -##file distutils-init.py -DISTUTILS_INIT = """ -eJytVl2L6zYQffevGBKK7XavKe3bhVBo78uFSyml0IdlEVpbTtR1JCMpm6S/vjOSY0v+uO1DDbs4 -0tF8nJk5sjz32jjQNpPhzd7H1ys3SqqjhcfCL1q18vgbN1YY2Kc/pQWlHXB4l8ZdeCfUO5x1c+nE -E1gNVwE1V3CxAqQDp6GVqgF3EmBd08nXLGukUfws4IDBVD13p2pYoS3rLk52ltF6hPhLS1XM4EUc -VsVYKzvBWPkE+WgmLzPZjkaUNmd6KVI3JRwWoRSLM6P98mMG+Dw4q+il8Ev07P7ATCNmRlfQ8/qN -HwVwB99Y4H0vMHAi6BWZUoEhoqXTNXdSK+A2LN6tE+fJ0E+7MhOdFSEM5lNgrJIKWXDF908wy87D -xE3UoHsxkegZTaHIHGNSSYfm+ntelpURvCnK7NEWBI/ap/b8Z1m232N2rj7B60V2DRM3B5NpaLSw -KnfwpvQVTviHOR+F88lhQyBAGlE7be6DoRNg9ldsG3218IHa6MRNU+tGBEYIggwafRk6yzsXDcVU -9Ua08kYxt+F3x12LRaQi52j0xx/ywFxrdMRqVevzmaummlIYEp0WsCAaX8cFb6buuLUTqEgQQ6/Q -04iWRoF38m/BdE8VtlBY0bURiB6KG1crpMZwc2fIjqWh+1UrkSLpWUIP8PySwLKv4qPGSVqDuMPy -dywQ+gS7L1irXVkm5pJsq3l+Ib1lMOvUrxI+/mBBY4KB+WpUtcO06RtzckNvQ6vYj1lGoZM2sdDG -fryJPYJVn/Cfka8XSqNaoLKhmOlqXMzW9+YBVp1EtIThZtOwzCRvMaARa+0xD0b2kcaJGwJsMbc7 -hLUfY4vKvsCOBdvDnyfuRbzmXRdGTZgPF7oGQkJACWVD22IMQdhx0npt5S2f+pXO+OwH6d+hwiS5 -7IJOjcK2emj1zBy1aONHByfAMoraw6WlrSIFTbGghqASoRCjVncYROFpXM4uYSqhGnuVeGvks4jz -cjnCoR5GnPW7KOh4maVbdFeoplgJ3wh3MSrAsv/QuMjOspnTKRl1fTYqqNisv7uTVnhF1GhoBFbp -lh+OcXN2riA5ZrYXtWxlfcDuC8U5kLoN3CCJYXGpesO6dx6rU0zGMtjU6cNlmW0Fid8Sja4ZG+Z3 -fTPbyj+mZnZ2wSQK8RaT9Km0ySRuLpm0DkUUL0ra3WQ2BgGJ7v9I9SKqNKZ/IR4R28RHm+vEz5ic -nZ2IH7bfub8pU1PR3gr10W7xLTfHh6Z6bgZ7K14G7Mj/1z5J6MFo6V5e07H0Ou78dTyeI+mxKOpI -eC2KMSj6HKxd6Uudf/n886fPv+f++x1lbASlmjQuPz8OvGA0j7j2eCu/4bcW6SFeCuNJ0W1GQHI5 -iwC9Ey0bjtHd9P4dPA++XxLnZDVuxvFEtlm3lf5a2c02u2LRYXHH/AOs8pIa -""".decode("base64").decode("zlib") - -##file distutils.cfg -DISTUTILS_CFG = """ -eJxNj00KwkAMhfc9xYNuxe4Ft57AjYiUtDO1wXSmNJnK3N5pdSEEAu8nH6lxHVlRhtDHMPATA4uH -xJ4EFmGbvfJiicSHFRzUSISMY6hq3GLCRLnIvSTnEefN0FIjw5tF0Hkk9Q5dRunBsVoyFi24aaLg -9FDOlL0FPGluf4QjcInLlxd6f6rqkgPu/5nHLg0cXCscXoozRrP51DRT3j9QNl99AP53T2Q= -""".decode("base64").decode("zlib") - -##file activate_this.py -ACTIVATE_THIS = """ -eJyNUk2L3DAMvftXiCxLEphmSvc2MIcu9NaWHnopwxCcRNlRN7GD7clM/n0lp5mPZQs1JLb8pKcn -WUmSPE9w9GReAM9Yt9RhFg7kSzmtoKE6ZGU0ynJ7AfIcJnuEE3Wd0nWgUQcEQWEkF466QzMCf+Ss -6dGEQqmfgtbaQIWcDxs4HdBElv7og1wBg3gmH0TMjykcrAEyAd3gkP8rMDaocMDbHBWZ9RBdVZIk -SgU3bRTwWjQrPNc4BPiue/zinHUz7DRxws/eowtkTUSyiMhKfi2y3NHMdXX0itcOpYMOh3Ww61g8 -luJSDFP6tmH3ftyki2eeJ7mifrAugJ/8crReqUqztC0fC4kuGnKGxWf/snXlZb8kzXMmboW0GDod -Wut62G4hPZF5+pTO5XtiKYOuX/UL+ptcvy2ZTPKvIP1KFdeTiuuHxTXNFXYe/5+km0nmJ3r0KTxG -YSM6z23fbZ7276Tg9x5LdiuFjok7noks1sP2tWscpeRX6KaRnRuT3WnKlQQ51F3JlC2dmSvSRENd -j3wvetUDfLOjDDLPYtPwjDJb7yHYeNXyMPMLtdEQKRtl8HQrdLdX3O4YxZP7RvfcNH6ZCPMsi8td -qZvLAN7yFnoY0DSZhOUXj4WWy+tZ8190ud1tPu5Zzy2N+gOGaVfA -""".decode("base64").decode("zlib") - -if __name__ == '__main__': - main() - -## TODO: -## Copy python.exe.manifest -## Monkeypatch distutils.sysconfig diff --git a/catalogapp/jsbuild/main.cfg b/catalogapp/jsbuild/main.cfg deleted file mode 100644 index a082952f97..0000000000 --- a/catalogapp/jsbuild/main.cfg +++ /dev/null @@ -1,72 +0,0 @@ -[catalogapp.js] -root = - ../src/main/webapp/lib/Ext.ux/lib - ../src/main/webapp/lib/externals/openlayers/lib - ../src/main/webapp/lib/externals/geoext/lib - ../src/main/webapp/lib/externals/ext/src - ../src/main/webapp/app/js -first = - OpenLayers/SingleFile.js - OpenLayers.js - OpenLayers/BaseTypes.js - OpenLayers/BaseTypes/Class.js - OpenLayers/Util.js - OpenLayers/Lang.js - OpenLayers/Console.js - OpenLayers/BaseTypes/Pixel.js - OpenLayers/BaseTypes/Bounds.js - OpenLayers/BaseTypes/LonLat.js - OpenLayers/BaseTypes/Element.js - OpenLayers/BaseTypes/Size.js - overrides/override-ext-ajax.js -include = - GEOR.js -exclude = - Ext.ux.js - Ext.ux/SingleFile.js - GeoExt.js - GeoExt/SingleFile.js - -[lang/fr.js] -root = - ../src/main/webapp/lib/externals/openlayers/lib - ../src/main/webapp/lib/externals/ext/src - ../src/main/webapp/app/js -#~ first = - #~ OpenLayers/Lang/fr.js -include = - GEOR_Lang/fr.js - locale/ext-lang-fr.js - -[lang/en.js] -root = - ../src/main/webapp/lib/externals/openlayers/lib - ../src/main/webapp/lib/externals/ext/src - ../src/main/webapp/app/js -#~ first = - #~ OpenLayers/Lang/en.js -include = - GEOR_Lang/en.js - locale/ext-lang-en.js - -[lang/es.js] -root = - ../src/main/webapp/lib/externals/openlayers/lib - ../src/main/webapp/lib/externals/ext/src - ../src/main/webapp/app/js -#~ first = - #~ OpenLayers/Lang/es.js -include = - GEOR_Lang/es.js - locale/ext-lang-es.js - -[lang/de.js] -root = - ../src/main/webapp/lib/externals/openlayers/lib - ../src/main/webapp/lib/externals/ext/src - ../src/main/webapp/app/js -#~ first = - #~ OpenLayers/Lang/de.js -include = - GEOR_Lang/de.js - locale/ext-lang-de.js diff --git a/catalogapp/jsbuild/util/gen-go-jstools.py b/catalogapp/jsbuild/util/gen-go-jstools.py deleted file mode 100644 index 50177e256f..0000000000 --- a/catalogapp/jsbuild/util/gen-go-jstools.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python -"""Generate go-jstools.py""" -import sys -import textwrap -import virtualenv - -filename = 'go-jstools.py' - -after_install = """\ -import os, subprocess -def after_install(options, home_dir): - etc = join(home_dir, 'etc') - ## TODO: this should all come from distutils - ## like distutils.sysconfig.get_python_inc() - if sys.platform == 'win32': - lib_dir = join(home_dir, 'Lib') - bin_dir = join(home_dir, 'Scripts') - elif is_jython: - lib_dir = join(home_dir, 'Lib') - bin_dir = join(home_dir, 'bin') - else: - lib_dir = join(home_dir, 'lib', py_version) - bin_dir = join(home_dir, 'bin') - - if not os.path.exists(etc): - os.makedirs(etc) - subprocess.call([join(bin_dir, 'easy_install'), 'JSTools==%s']) -""" - - -def generate(filename, version): - # what's commented out below comes from go-pylons.py - - #path = version - #if '==' in version: - # path = version[:version.find('==')] - #output = virtualenv.create_bootstrap_script( - # textwrap.dedent(after_install % (path, version))) - - output = virtualenv.create_bootstrap_script( - textwrap.dedent(after_install % version)) - fp = open(filename, 'w') - fp.write(output) - fp.close() - - -def main(): - if len(sys.argv) != 2: - print >> sys.stderr, 'usage: %s version' % sys.argv[0] - sys.exit(1) - generate(filename, sys.argv[1]) - - -if __name__ == '__main__': - main() diff --git a/catalogapp/pom.xml b/catalogapp/pom.xml deleted file mode 100644 index 07cb7d7a06..0000000000 --- a/catalogapp/pom.xml +++ /dev/null @@ -1,365 +0,0 @@ - - - 4.0.0 - - org.georchestra - root - 15.12-SNAPSHOT - - catalogapp - catalogapp - war - http://www.georchestra.org - - catalogapp - 3.1.0.RELEASE - 1.6.1 - 22.0.3-BETA - 3.1.0.RELEASE - - - - org.tuckey - urlrewritefilter - 3.0.4 - - - org.springframework - spring-mock - 2.0.3 - test - - - org.springframework - spring-webmvc - ${spring.version} - compile - - - log4j - log4j - 1.2.16 - jar - - - log4j - apache-log4j-extras - 1.1 - jar - runtime - - - - javax.servlet - servlet-api - 2.5 - provided - - - javax.servlet.jsp - jsp-api - 2.1 - provided - - - javax.servlet - jstl - 1.2 - - - - org.georchestra - georchestra-commons - ${project.version} - - - - - - org.eclipse.jetty - jetty-maven-plugin - 9.2.11.v20150529 - - /catalogapp/ - 5 - - 8281 - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - org.codehaus.groovy.maven - gmaven-plugin - - - org.georchestra - config - ${project.version} - ${server} - - - - - maven-antrun-plugin - - - serverConfigCopy - generate-sources - - - - - - - - - - - - - - - - - run - - - - - - org.apache.maven.plugins - maven-war-plugin - - catalogapp - - - - org.mortbay.jetty - maven-jetty-plugin - 6.1.10 - - 5 - - src/main/webapp/js/app - - foo - 9999 - - - - org.codehaus.mojo - tomcat-maven-plugin - 1.0-beta-1 - - /${project.build.finalName} - - - - - org.codehaus.mojo - exec-maven-plugin - 1.1 - - - process-sources - - exec - - - - - jsbuild/${jsbuild.command} - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - ${java-version} - ${java-version} - - - - - - - georchestra - - - georchestra - - - - - Windows - - - - Windows - - - - build.bat - - - - - Unix - - - - unix - - - - build.sh - - - - Macintosh - - - - mac - - - - build.sh - - - - debianPackage - - - - org.apache.maven.plugins - maven-war-plugin - 2.1.1 - - generic - **/docs/**,**/openlayers/examples/**,**/openlayers/tests/**,**/ext/examples/** - - - - maven-resources-plugin - 2.3 - - - copy-deb-resources - process-resources - copy-resources - - true - ${basedir}/target/deb - - - src/deb/resources - - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - 1.6 - - - fix-permissions - package - - - - - - - - - - - - - run - - - - - net.sf.debian-maven - debian-maven-plugin - 1.0.6 - - georchestra-catalogapp - geOrchestra Catalogapp (CSW frontend) - geOrchestra - PSC - psc@georchestra.org - true - - - - - - - rpmPackage - - - - org.apache.maven.plugins - maven-war-plugin - 2.1.1 - - generic - **/docs/**,**/openlayers/examples/**,**/openlayers/tests/** - - - - org.codehaus.mojo - rpm-maven-plugin - 2.1.3 - - - generate-rpm - - rpm - - - - - georchestra-${project.artifactId} - UTF-8 - Applications/Internet - ${rpm.gpg.key} - - - /usr/share/lib/georchestra-${project.artifactId} - - - ${project.build.directory} - - ${project.artifactId}-generic.war - - - - - - / - - - ${basedir}/src/deb/resources - - - - - - - - - - - diff --git a/catalogapp/src/deb/resources/etc/georchestra/catalogapp/catalogapp.properties b/catalogapp/src/deb/resources/etc/georchestra/catalogapp/catalogapp.properties deleted file mode 100644 index e38eee4653..0000000000 --- a/catalogapp/src/deb/resources/etc/georchestra/catalogapp/catalogapp.properties +++ /dev/null @@ -1,3 +0,0 @@ -language=fr -instance=geOrchestra -headerHeight=90 diff --git a/catalogapp/src/deb/resources/etc/georchestra/catalogapp/js/GEOR_custom.js b/catalogapp/src/deb/resources/etc/georchestra/catalogapp/js/GEOR_custom.js deleted file mode 100644 index 677728ffd2..0000000000 --- a/catalogapp/src/deb/resources/etc/georchestra/catalogapp/js/GEOR_custom.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Sample geOrchestra catlogapp config file - * - * Instructions: - * uncomment lines you wish to modify and - * modify the corresponding values to suit your needs. - */ - -Ext.namespace("GEOR"); - -GEOR.custom = { - - /** - * Constant: HEADER_HEIGHT - * Integer value representing the header height, as set in the shared maven filters - * Defaults to 90 - */ - HEADER_HEIGHT: 90, - - /***** Beginning of config options which can be set in this file *****/ - - /** - * Constant: GEONETWORK_URL - * The URL to the GeoNetwork server. - * Defaults to "/geonetwork/srv/fre" - */ - GEONETWORK_URL: "https://georchestra.mydomain.org/geonetwork/srv/fre", - - /** - * Constant: VIEWER_URL - * The URL to Mapfishapp - * Defaults to "/mapfishapp/" - */ - VIEWER_URL: "https://georchestra.mydomain.org/mapfishapp/", - - /** - * Constant: EXTRACTOR_URL - * The URL to Extractorapp - * Defaults to "/extractorapp/" - */ - EXTRACTOR_URL: "https://georchestra.mydomain.org/extractorapp/" - - /** - * Constant: MAP_DOTS_PER_INCH - * {Float} Sets the resolution used for scale computation. - * Defaults to GeoServer defaults, which is 25.4 / 0.28 - */ - //,MAP_DOTS_PER_INCH: 25.4 / 0.28 - - // No trailing comma for the last line (or IE will complain) -} diff --git a/catalogapp/src/deb/resources/etc/georchestra/catalogapp/log4j/log4j.properties b/catalogapp/src/deb/resources/etc/georchestra/catalogapp/log4j/log4j.properties deleted file mode 100644 index 9c8baf572d..0000000000 --- a/catalogapp/src/deb/resources/etc/georchestra/catalogapp/log4j/log4j.properties +++ /dev/null @@ -1,11 +0,0 @@ -log4j.rootLogger=WARN, R - -log4j.logger.catalogapp=WARN, R - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = /tmp/catalogapp.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = /tmp/catalogapp.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n diff --git a/catalogapp/src/main/filtered-resources/WEB-INF/jsp/index.jsp b/catalogapp/src/main/filtered-resources/WEB-INF/jsp/index.jsp deleted file mode 100644 index 09ba7ebc9e..0000000000 --- a/catalogapp/src/main/filtered-resources/WEB-INF/jsp/index.jsp +++ /dev/null @@ -1,158 +0,0 @@ -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> -<%@ page language="java" %> -<%@ page import="java.util.*" %> -<%@ page import="org.springframework.web.context.support.WebApplicationContextUtils" %> -<%@ page import="org.springframework.context.ApplicationContext" %> -<%@ page import="org.springframework.web.servlet.support.RequestContextUtils" %> -<%@ page import="org.georchestra.commons.configuration.GeorchestraConfiguration" %> -<%@ page import="org.georchestra.catalogapp.Utf8ResourceBundle" %> -<%@ page contentType="text/html; charset=UTF-8"%> -<%@ page pageEncoding="UTF-8"%> -<%@ page isELIgnored="false" %> -<% - -Boolean anonymous = true; -Boolean admin = false; -Boolean editor = false; - -String instanceName = null; -String defaultLanguage = null; -String georCustomPath = "app/js/GEOR_custom.js"; - -try { - ApplicationContext ctx = RequestContextUtils.getWebApplicationContext(request); - instanceName = ctx.getBean(GeorchestraConfiguration.class).getProperty("instance"); - defaultLanguage = ctx.getBean(GeorchestraConfiguration.class).getProperty("language"); - if (ctx.getBean(GeorchestraConfiguration.class).activated()) { - georCustomPath = "ws" + georCustomPath ; - } -} catch (Exception e) {} - -String lang = request.getParameter("lang"); -if (lang == null || (!lang.equals("en") && !lang.equals("es") && !lang.equals("fr") && !lang.equals("de"))) { - if (defaultLanguage != null) { - lang = defaultLanguage; - } - else { - lang = "${language}"; - } -} - -if (instanceName == null) { - instanceName = "${instance}"; -} - -Locale l = new Locale(lang); -ResourceBundle resource = Utf8ResourceBundle.getBundle("catalogapp.i18n.index",l); -javax.servlet.jsp.jstl.core.Config.set( - request, - javax.servlet.jsp.jstl.core.Config.FMT_LOCALIZATION_CONTEXT, - new javax.servlet.jsp.jstl.fmt.LocalizationContext(resource) -); - -String sec_roles = request.getHeader("sec-roles"); -if(sec_roles != null) { - String[] roles = sec_roles.split(";"); - for (int i = 0; i < roles.length; i++) { - if (roles[i].equals("ROLE_SV_ADMIN")) { - admin = true; - } - if (roles[i].equals("ROLE_SV_EDITOR") || roles[i].equals("ROLE_SV_REVIEWER") || roles[i].equals("ROLE_SV_ADMIN")) { - editor = true; - anonymous = false; - } - if (roles[i].equals("ROLE_SV_USER")) { - anonymous = false; - } - } -} -%> - - - - - - <fmt:message key="title.catalogue"/> - <%= instanceName %> - - - - - - - - - - <%@ include file="header.jsp" %> - - -
- <fmt:message key='loading'/> - -
-
-

-
- - - - - - - - - <%@ include file="debug-includes.jsp" %> - - - - - - - - - - - - diff --git a/catalogapp/src/main/java/org/georchestra/catalogapp/DefaultController.java b/catalogapp/src/main/java/org/georchestra/catalogapp/DefaultController.java deleted file mode 100644 index 6cec3fc6b2..0000000000 --- a/catalogapp/src/main/java/org/georchestra/catalogapp/DefaultController.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.georchestra.catalogapp; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.servlet.ModelAndView; - - - -@Controller -@RequestMapping("/home") -public class DefaultController { - - @RequestMapping(method = RequestMethod.POST) - public ModelAndView handlePOSTRequest(HttpServletRequest request, HttpServletResponse response) - throws IOException { - return new ModelAndView("index"); - } - @RequestMapping(method = RequestMethod.GET) - public ModelAndView handleGETRequest(HttpServletRequest request, HttpServletResponse response) { - return new ModelAndView("index"); - } - -} diff --git a/catalogapp/src/main/java/org/georchestra/catalogapp/Proxy.java b/catalogapp/src/main/java/org/georchestra/catalogapp/Proxy.java deleted file mode 100644 index 89057f9077..0000000000 --- a/catalogapp/src/main/java/org/georchestra/catalogapp/Proxy.java +++ /dev/null @@ -1,438 +0,0 @@ -package org.georchestra.catalogapp; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.zip.DeflaterInputStream; -import java.util.zip.DeflaterOutputStream; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; - -@Controller -@RequestMapping("/ogcproxy/*") -public class Proxy { - - /** - * List of allowed hosts. - * If empty, means everyone's allowed - */ - public static final String[] _allowedHosts = - { - }; - - /** - * List of valid content types - */ - public static final String[] _validContentTypes = - { - "application/xml", "text/xml", - "application/vnd.ogc.se_xml", // OGC Service Exception - "application/vnd.ogc.se+xml", // OGC Service Exception - "application/vnd.ogc.success+xml", // OGC Success (SLD Put) - "application/vnd.ogc.wms_xml", // WMS Capabilities - "application/vnd.ogc.context+xml", // WMC - "application/vnd.ogc.gml", // GML - "application/vnd.ogc.sld+xml", // SLD - "application/vnd.google-earth.kml+xml", // KML - }; - - /** - * Proxy entry point. Accessible via POST. - * @param request. Must contains a "url" parameter pointing to the remote host - * @param response. Contains in its content response from remote host - * @param sURL. Automatically filled by the Spring url mapping - */ - @RequestMapping(method=RequestMethod.POST) - public void handlePOSTRequest(HttpServletRequest request, HttpServletResponse response, @RequestParam("url") String sURL) { - handleRequest(request, response, sURL); - } - - /** - * Proxy entry point. Accessible via GET. - * @param request. Must contains a "url" parameter pointing to the remote host - * @param response. Contains in its content response from remote host - * @param sURL. Automatically filled by the Spring url mapping - */ - @RequestMapping(method=RequestMethod.GET) - public void handleGETRequest(HttpServletRequest request, HttpServletResponse response, @RequestParam("url") String sURL) { - handleRequest(request, response, sURL); - } - - /** - * - */ - private void handleRequest(HttpServletRequest request, HttpServletResponse response, String sURL) { - try { - - URL url = null; - try { - url = new URL(sURL); - } - catch (MalformedURLException e) { // not an url - response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); - return; - } - - // HTTP protocol is required - if (!"http".equalsIgnoreCase(url.getProtocol())) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST, - "HTTP protocol expected. \"" + url.getProtocol() + "\" used."); - return; - } - - // check if proxy must filter on final host - if (isFilteringOnFinalHost()) { - // requested host has to be among the list (_allowedHosts) - if (!isHostAllowed(url.getHost())) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST, - "Host \"" + url.getHost() + "\" is not allowed to be requested"); - return; - } - } - - String requestMethod = request.getMethod(); - - // open communication between proxy and final host - // all actions before the connection can be taken now - HttpURLConnection connectionWithFinalHost = (HttpURLConnection) url.openConnection(); - - // set request method - connectionWithFinalHost.setRequestMethod(requestMethod); - - // set doOutput to true if we are POSTing - if (requestMethod.equalsIgnoreCase("POST")) { - connectionWithFinalHost.setDoOutput(true); - } - - // copy headers from client's request to request that will be send to the final host - copyHeadersToConnection(request, connectionWithFinalHost); - connectionWithFinalHost.setRequestProperty("Accept-Encoding", ""); - - // connect to remote host - // interactions with the resource are enabled now - connectionWithFinalHost.connect(); - - if (requestMethod.equalsIgnoreCase("POST")) { - ServletInputStream in = request.getInputStream(); - OutputStream out = connectionWithFinalHost.getOutputStream(); - byte[] buf = new byte[1024]; // read maximum 1024 bytes - int len; // number of bytes read from the stream - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - } - - // get content type - String contentType = connectionWithFinalHost.getContentType(); - if (contentType == null) { - response.sendError(HttpServletResponse.SC_FORBIDDEN, - "Host url has been validated by proxy but content type given by remote host is null"); - return; - } - - // content type has to be valid - if (!isContentTypeValid(contentType)) { - - if (connectionWithFinalHost.getResponseMessage() != null) { - if (connectionWithFinalHost.getResponseMessage().equalsIgnoreCase("Not Found")) { - // content type was not valid because it was a not found page (text/html) - response.sendError(HttpServletResponse.SC_NOT_FOUND, "Remote host not found"); - return; - } - } - - response.sendError(HttpServletResponse.SC_FORBIDDEN, - "The content type of the remote host's response \"" + contentType - + "\" is not allowed by the proxy rules"); - return; - } - - // send remote host's response to client - - /* Here comes the tricky part because some host send files without the charset - * in the header, therefore we do not know how they are text encoded. It can result - * in serious issues on IE browsers when parsing those files. - * There is a workaround which consists to read the encoding within the file. It is made - * possible because this proxy mainly forwards xml files. They all have the encoding - * attribute in the first xml node. - * - * This is implemented as follows: - * - * A. The content type provides a charset: - * Nothing special, just send back the stream to the client - * B. There is no charset provided: - * The encoding has to be extracted from the file. - * The file is read in ASCII, which is common to many charsets, - * like that the encoding located in the first not can be retrieved. - * Once the charset is found, the content-type header is overridden and the - * charset is appended. - * - * /!\ Special case: whenever data are compressed in gzip/deflate the stream has to - * be uncompressed and compressed - */ - - boolean isCharsetKnown = connectionWithFinalHost.getContentType().toLowerCase().contains("charset"); - String contentEncoding = getContentEncoding(connectionWithFinalHost.getHeaderFields()); - - // copy headers from the remote server's response to the response to send to the client - if (isCharsetKnown) { - copyHeadersFromConnectionToResponse(response, connectionWithFinalHost); - } else { - // copy everything except Content-Type header - // because we need to concatenate the charset later - copyHeadersFromConnectionToResponse(response, connectionWithFinalHost, new String[] {"Content-Type"}); - } - - InputStream streamFromServer = null; - OutputStream streamToClient = null; - if(contentEncoding == null || isCharsetKnown) { - // A simple stream can do the job for data that is not in content encoded - // but also for data content encoded with a known charset - streamFromServer = connectionWithFinalHost.getInputStream(); - streamToClient = response.getOutputStream(); - } - else if ("gzip".equalsIgnoreCase(contentEncoding) && !isCharsetKnown) { - // the charset is unknown and the data are compressed in gzip - // we add the gzip wrapper to be able to read/write the stream content - streamFromServer = new GZIPInputStream(connectionWithFinalHost.getInputStream()); - streamToClient = new GZIPOutputStream(response.getOutputStream()); - } - else if("deflate".equalsIgnoreCase(contentEncoding) && !isCharsetKnown) { - // same but with deflate - streamFromServer = new DeflaterInputStream(connectionWithFinalHost.getInputStream()); - streamToClient = new DeflaterOutputStream(response.getOutputStream()); - } else { - throw new UnsupportedOperationException("Please handle the stream when it is encoded in " + contentEncoding); - } - - byte[] buf = new byte[1024]; // read maximum 1024 bytes - int len; // number of bytes read from the stream - boolean first = true; // helps to find the encoding once and only once - String s = ""; // piece of file that should contain the encoding - while ((len = streamFromServer.read(buf)) > 0) { - - if (first && !isCharsetKnown) { - // charset is unknown try to find it in the file content - for(int i=0; i < len; i++) { - s += (char) buf[i]; // get the beginning of the file as ASCII - } - - // s has to be long enough to contain the encoding - if(s.length() > 200) { - String charset = getCharset(s); // extract charset - - if (charset == null) { - // the charset cannot be found, IE users must be warned - // that the request cannot be fulfilled, nothing good would happen otherwise - if(request.getHeader("User-Agent").toLowerCase().contains("msie")) { - response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE, - "Charset of the response is unknown"); - streamFromServer.close(); - connectionWithFinalHost.disconnect(); - streamToClient.close(); - return; - } - } else { - // override content-type header and add the charset found - response.addHeader("Content-Type", - connectionWithFinalHost.getContentType() - + ";charset=" + charset); - first = false; // we found the encoding, don't try to do it again - } - } - } - - // for everyone, the stream is just forwarded to the client - streamToClient.write(buf, 0, len); - } - - streamFromServer.close(); - connectionWithFinalHost.disconnect(); - streamToClient.close(); - } - catch (IOException e) { - // connection problem with the host - e.printStackTrace(); - } - } - - /** - * Extract the encoding from a string which is the header node of an xml file - * @param header String that should contain the encoding attribute and its value - * @return the charset. null if not found - */ - private String getCharset(String header) { - Pattern pattern = null; - String charset = null; - try { - // use a regexp but we could also use string functions such as - // indexOf... - pattern = Pattern.compile("encoding=(['\"])([A-Za-z]([A-Za-z0-9._]|-)*)"); - } catch (Exception e) { - throw new RuntimeException("expression syntax invalid"); - } - - Matcher matcher = pattern.matcher(header); - if (matcher.find()) { - String encoding = matcher.group(); - charset = encoding.split("['\"]")[1]; - } - - return charset; - } - - /** - * Gets the encoding of the content sent by the remote host: extracts the - * content-encoding header - * @param headerFields headers of the HttpURLConnection - * @return null if not exists otherwise name of the encoding (gzip, deflate...) - */ - private String getContentEncoding(Map> headerFields) { - for (Iterator i = headerFields.keySet().iterator() ; i.hasNext() ; ) { - String headerName = i.next(); - if(headerName != null) { - if("Content-Encoding".equalsIgnoreCase(headerName)) { - List valuesList = headerFields.get(headerName); - StringBuilder sBuilder = new StringBuilder(); - for(String value : valuesList) { - sBuilder.append(value); - } - - return sBuilder.toString().toLowerCase(); - } - } - } - return null; - } - - /** - * Copy headers from the connection to the response - * @param response to copy headers in - * @param uc contains headers to copy - * @param ignoreList list of headers that mustn't be copied - */ - private void copyHeadersFromConnectionToResponse(HttpServletResponse response, HttpURLConnection uc, String... ignoreList) { - Map> map = uc.getHeaderFields(); - for (Iterator i = map.keySet().iterator() ; i.hasNext() ; ) { - - String headerName = i.next(); - - if(! isInIgnoreList(headerName, ignoreList)) { - - // concatenate all values from the header - List valuesList = map.get(headerName); - StringBuilder sBuilder = new StringBuilder(); - for(String value : valuesList) { - sBuilder.append(value); - } - - // add header to HttpServletResponse object - if (headerName != null) { - if("Transfer-Encoding".equalsIgnoreCase(headerName) && "chunked".equalsIgnoreCase(sBuilder.toString())) { - // do not write this header because Tomcat already assembled the chunks itself - continue; - } - response.addHeader(headerName, sBuilder.toString()); - } - } - } - } - - /** - * Helper function to detect if a specific header is in a given ignore list - * @param headerName - * @param ignoreList - * @return true: in, false: not in - */ - private boolean isInIgnoreList(String headerName, String[] ignoreList) { - if (headerName == null) return false; - - for (String headerToIgnore : ignoreList) { - if (headerName.equalsIgnoreCase(headerToIgnore)) - return true; - } - return false; - } - - /** - * Copy client's headers in the request to send to the final host - * Trick the host by hiding the proxy indirection and keep useful headers information - * @param request - * @param uc. Contains now headers from client request except Host - */ - protected void copyHeadersToConnection(HttpServletRequest request, HttpURLConnection uc) { - - for (Enumeration enumHeader=request.getHeaderNames(); enumHeader.hasMoreElements();) { - String headerName = (String)enumHeader.nextElement(); - String headerValue = request.getHeader(headerName); - - // copy every header except host - if (!"host".equalsIgnoreCase(headerName)) { - uc.setRequestProperty(headerName, headerValue); - } - } - } - - /** - * Check if a particular host can be requested - * @param host - host name - * @return true: allowed; false: not allowed - */ - protected boolean isHostAllowed(final String host) { - - if (!isFilteringOnFinalHost()) { - // Proxy doesn't filter anyone - return true; - } - for (String allowedHost : Proxy._allowedHosts) { - if (allowedHost.equals(host)) { - return true; - } - } - return false; - } - - /** - * Determine if Proxy filters on final host according to the list of allowed ones - */ - protected boolean isFilteringOnFinalHost() { - - return (Proxy._allowedHosts.length == 0)?(false):(true); - } - - /** - * Check if the content type is accepted by the proxy - * @param contentType - * @return true: valid; false: not valid - */ - protected boolean isContentTypeValid(final String contentType) { - - // focus only on type, not on the text encoding - String type = contentType.split(";")[0]; - for (String validTypeContent : Proxy._validContentTypes) { - if (validTypeContent.equals(type)) { - return true; - } - } - return false; - } - -} \ No newline at end of file diff --git a/catalogapp/src/main/java/org/georchestra/catalogapp/Utf8ResourceBundle.java b/catalogapp/src/main/java/org/georchestra/catalogapp/Utf8ResourceBundle.java deleted file mode 100644 index cf4409f740..0000000000 --- a/catalogapp/src/main/java/org/georchestra/catalogapp/Utf8ResourceBundle.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.georchestra.catalogapp; -import java.io.UnsupportedEncodingException; -import java.util.Enumeration; -import java.util.Locale; -import java.util.PropertyResourceBundle; -import java.util.ResourceBundle; - -public class Utf8ResourceBundle { - -public static final ResourceBundle getBundle(String baseName) { - ResourceBundle bundle = ResourceBundle.getBundle(baseName); - return createUtf8PropertyResourceBundle(bundle); -} - -public static final ResourceBundle getBundle(String baseName, Locale locale) { - ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale); - return createUtf8PropertyResourceBundle(bundle); -} - -public static ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader) { - ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale, loader); - return createUtf8PropertyResourceBundle(bundle); -} - -private static ResourceBundle createUtf8PropertyResourceBundle(ResourceBundle bundle) { - if (!(bundle instanceof PropertyResourceBundle)) return bundle; - - return new Utf8PropertyResourceBundle((PropertyResourceBundle)bundle); -} - -private static class Utf8PropertyResourceBundle extends ResourceBundle { - PropertyResourceBundle bundle; - - private Utf8PropertyResourceBundle(PropertyResourceBundle bundle) { - this.bundle = bundle; - } - /* (non-Javadoc) - * @see java.util.ResourceBundle#getKeys() - */ - public Enumeration getKeys() { - return bundle.getKeys(); - } - /* (non-Javadoc) - * @see java.util.ResourceBundle#handleGetObject(java.lang.String) - */ - protected Object handleGetObject(String key) { - String value = (String)bundle.getString(key); - if (value==null) return null; - try { - return new String (value.getBytes("ISO-8859-1"),"UTF-8") ; - } catch (UnsupportedEncodingException e) { - // Shouldn't fail - but should we still add logging message? - return null; - } - } - -} -} diff --git a/catalogapp/src/main/resources/catalogapp/i18n/index_de.properties b/catalogapp/src/main/resources/catalogapp/i18n/index_de.properties deleted file mode 100644 index 5d0f401fce..0000000000 --- a/catalogapp/src/main/resources/catalogapp/i18n/index_de.properties +++ /dev/null @@ -1,8 +0,0 @@ -### Translation file - -# index.jsp -title.catalogue=Katalog -loading=Lade... -default.help=Suchen Sie nach Stichwörtern oder Region um Geodaten zu finden. -need.javascript=Diese Anwendung benötigt Javascript. \ -Bitte aktivieren Sie es in Ihrem Browser. \ No newline at end of file diff --git a/catalogapp/src/main/resources/catalogapp/i18n/index_en.properties b/catalogapp/src/main/resources/catalogapp/i18n/index_en.properties deleted file mode 100644 index a6998f3856..0000000000 --- a/catalogapp/src/main/resources/catalogapp/i18n/index_en.properties +++ /dev/null @@ -1,8 +0,0 @@ -### Translation file - -# index.jsp -title.catalogue=Catalogue -loading=Loading... -default.help=You can find geographic data searching by keywords or by region. -need.javascript=This application needs Javascript support. \ -Please activate it in your browser. diff --git a/catalogapp/src/main/resources/catalogapp/i18n/index_es.properties b/catalogapp/src/main/resources/catalogapp/i18n/index_es.properties deleted file mode 100644 index a217c1e69a..0000000000 --- a/catalogapp/src/main/resources/catalogapp/i18n/index_es.properties +++ /dev/null @@ -1,9 +0,0 @@ -### Translation file - -# index.jsp -title.catalogue=Catálogo -loading=Cargando... -default.help=Puede encontrar datos geográficos buscando por palabras-clave o por región. -need.javascript=Esta aplicación necesita el soporte de Javascript. \ -Por favor activelo en su navegador. - diff --git a/catalogapp/src/main/resources/catalogapp/i18n/index_fr.properties b/catalogapp/src/main/resources/catalogapp/i18n/index_fr.properties deleted file mode 100644 index 3dcb64c84a..0000000000 --- a/catalogapp/src/main/resources/catalogapp/i18n/index_fr.properties +++ /dev/null @@ -1,9 +0,0 @@ -### Translation file - -# index.jsp -title.catalogue=Catalogue -loading=Chargement... -default.help=Trouvez des données géographiques en cherchant par mots clés ou par région. -need.javascript:Cette application nécessite le support de JavaScript par \ -votre navigateur. Merci de l'activer. - diff --git a/catalogapp/src/main/webapp/META-INF/MANIFEST.MF b/catalogapp/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index d91dddee44..0000000000 --- a/catalogapp/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,9 +0,0 @@ -Manifest-Version: 1.0 -Class-Path: aopalliance-1.0.jar jcl-over-slf4j-1.6.1.jar jstl-1.2.jar - log4j-1.2.15.jar slf4j-api-1.6.1.jar slf4j-log4j12-1.6.1.jar spring-a - op-3.0.5.RELEASE.jar spring-asm-3.0.5.RELEASE.jar spring-beans-3.0.5. - RELEASE.jar spring-context-3.0.5.RELEASE.jar spring-context-support-3 - .0.5.RELEASE.jar spring-core-3.0.5.RELEASE.jar spring-expression-3.0. - 5.RELEASE.jar spring-test-3.0.5.RELEASE.jar spring-web-3.0.5.RELEASE. - jar spring-webmvc-3.0.5.RELEASE.jar - diff --git a/catalogapp/src/main/webapp/WEB-INF/jsp/debug-includes.jsp b/catalogapp/src/main/webapp/WEB-INF/jsp/debug-includes.jsp deleted file mode 100644 index 523a7af976..0000000000 --- a/catalogapp/src/main/webapp/WEB-INF/jsp/debug-includes.jsp +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/catalogapp/src/main/webapp/WEB-INF/urlrewrite.xml b/catalogapp/src/main/webapp/WEB-INF/urlrewrite.xml deleted file mode 100644 index 778362becd..0000000000 --- a/catalogapp/src/main/webapp/WEB-INF/urlrewrite.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - ^/(ws)?/?$ - /ws/home - - - ^/ws/(.*)$ - /ws/$1 - - - ^/(.*)$ - /$1 - - - diff --git a/catalogapp/src/main/webapp/WEB-INF/web.xml b/catalogapp/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index d3f5b72b10..0000000000 --- a/catalogapp/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - UrlRewriteFilter - org.tuckey.web.filters.urlrewrite.UrlRewriteFilter - - - - confReloadCheckInterval - -1 - - - logLevel - WARN - - - - UrlRewriteFilter - /* - - - ws - - org.springframework.web.servlet.DispatcherServlet - - 1 - - - - ws - /ws/* - - - \ No newline at end of file diff --git a/catalogapp/src/main/webapp/WEB-INF/ws-servlet.xml b/catalogapp/src/main/webapp/WEB-INF/ws-servlet.xml deleted file mode 100644 index 840f7c4a34..0000000000 --- a/catalogapp/src/main/webapp/WEB-INF/ws-servlet.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/catalogapp/src/main/webapp/app/css/main.css b/catalogapp/src/main/webapp/app/css/main.css deleted file mode 100644 index 30f3cce480..0000000000 --- a/catalogapp/src/main/webapp/app/css/main.css +++ /dev/null @@ -1,67 +0,0 @@ -#waiter { - position:absolute; - top:0px; - left:50%; - margin-left: -62px; - z-index:100; - visibility:hidden; -} -#waiter span { - background:#EFAE00 none repeat scroll 0 0; - padding:2px; - border-radius: 0 0 4px 4px; -} - -#dataview-contentel { - padding: 5px; -} - -.westpanel .x-btn button, -.centerbbar .x-btn button { - color: #333333; - font: 16px arial,tahoma,verdana,helvetica; -} -.westpanel .bigbtn { - width: 100px !important; - height: 40px; -} - -.x-list div.x-view-over { - background-color: #eee; -} -.x-list div.x-view-selected { - background-color: #9ab3c5; -} - -.x-list div.x-view-item { - border:1px solid grey; - margin: 5px; - padding: 5px; - cursor: pointer; -} - -.geor-btn-search { - background-image: url(../img/famfamfam/cog.gif) !important; -} -.geor-btn-reset { - background-image: url(../img/famfamfam/cross.gif) !important; -} - -.olControlAttribution { - bottom: 2px; - right: 2px; - font-size: 8px; -} -.olHandlerBoxZoomBox { - border: 2px solid red; - position: absolute; - background-color: white; - opacity: 0.50; - font-size: 1px; - filter: alpha(opacity=50); -} - -/* hide disabled buttons in toolbar */ -.x-toolbar .x-item-disabled { - display:none; -} \ No newline at end of file diff --git a/catalogapp/src/main/webapp/app/img/famfamfam/cog.gif b/catalogapp/src/main/webapp/app/img/famfamfam/cog.gif deleted file mode 100644 index cb8e6a90bd..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/famfamfam/cog.gif and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/famfamfam/cross.gif b/catalogapp/src/main/webapp/app/img/famfamfam/cross.gif deleted file mode 100644 index 2dfa30166c..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/famfamfam/cross.gif and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/loading.gif b/catalogapp/src/main/webapp/app/img/loading.gif deleted file mode 100644 index b36b555b4f..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/loading.gif and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/cloud-popup-relative.png b/catalogapp/src/main/webapp/app/img/openlayers/cloud-popup-relative.png deleted file mode 100755 index 9db138abe9..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/cloud-popup-relative.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/drag-rectangle-off.png b/catalogapp/src/main/webapp/app/img/openlayers/drag-rectangle-off.png deleted file mode 100644 index fc6daf4dc9..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/drag-rectangle-off.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/drag-rectangle-on.png b/catalogapp/src/main/webapp/app/img/openlayers/drag-rectangle-on.png deleted file mode 100644 index 7f783ce133..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/drag-rectangle-on.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/east-mini.png b/catalogapp/src/main/webapp/app/img/openlayers/east-mini.png deleted file mode 100644 index 6289ad055f..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/east-mini.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/layer-switcher-maximize.png b/catalogapp/src/main/webapp/app/img/openlayers/layer-switcher-maximize.png deleted file mode 100644 index 317c4e71b8..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/layer-switcher-maximize.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/layer-switcher-minimize.png b/catalogapp/src/main/webapp/app/img/openlayers/layer-switcher-minimize.png deleted file mode 100644 index 400b0671ae..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/layer-switcher-minimize.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/marker-blue.png b/catalogapp/src/main/webapp/app/img/openlayers/marker-blue.png deleted file mode 100644 index 83a90b4c85..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/marker-blue.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/marker-gold.png b/catalogapp/src/main/webapp/app/img/openlayers/marker-gold.png deleted file mode 100644 index 2ff9ec5281..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/marker-gold.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/marker-green.png b/catalogapp/src/main/webapp/app/img/openlayers/marker-green.png deleted file mode 100644 index 17168f1b91..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/marker-green.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/marker.png b/catalogapp/src/main/webapp/app/img/openlayers/marker.png deleted file mode 100644 index ccd1913672..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/marker.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/measuring-stick-off.png b/catalogapp/src/main/webapp/app/img/openlayers/measuring-stick-off.png deleted file mode 100644 index 70c2dffb18..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/measuring-stick-off.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/measuring-stick-on.png b/catalogapp/src/main/webapp/app/img/openlayers/measuring-stick-on.png deleted file mode 100644 index cdb8f345b9..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/measuring-stick-on.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/north-mini.png b/catalogapp/src/main/webapp/app/img/openlayers/north-mini.png deleted file mode 100644 index 1dea0b3110..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/north-mini.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/panning-hand-off.png b/catalogapp/src/main/webapp/app/img/openlayers/panning-hand-off.png deleted file mode 100644 index 4c912aca66..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/panning-hand-off.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/panning-hand-on.png b/catalogapp/src/main/webapp/app/img/openlayers/panning-hand-on.png deleted file mode 100644 index 6094c64e7f..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/panning-hand-on.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/slider.png b/catalogapp/src/main/webapp/app/img/openlayers/slider.png deleted file mode 100644 index e7b9fa2ee6..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/slider.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/south-mini.png b/catalogapp/src/main/webapp/app/img/openlayers/south-mini.png deleted file mode 100644 index b23f4aa594..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/south-mini.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/west-mini.png b/catalogapp/src/main/webapp/app/img/openlayers/west-mini.png deleted file mode 100644 index 1441844bb1..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/west-mini.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/zoom-minus-mini.png b/catalogapp/src/main/webapp/app/img/openlayers/zoom-minus-mini.png deleted file mode 100644 index 21fbcbd5f5..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/zoom-minus-mini.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/zoom-plus-mini.png b/catalogapp/src/main/webapp/app/img/openlayers/zoom-plus-mini.png deleted file mode 100644 index f598da5e27..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/zoom-plus-mini.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/zoom-world-mini.png b/catalogapp/src/main/webapp/app/img/openlayers/zoom-world-mini.png deleted file mode 100644 index d030878060..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/zoom-world-mini.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/openlayers/zoombar.png b/catalogapp/src/main/webapp/app/img/openlayers/zoombar.png deleted file mode 100644 index 959f01a93d..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/openlayers/zoombar.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/img/spinner-large.gif b/catalogapp/src/main/webapp/app/img/spinner-large.gif deleted file mode 100644 index dada8aab7b..0000000000 Binary files a/catalogapp/src/main/webapp/app/img/spinner-large.gif and /dev/null differ diff --git a/catalogapp/src/main/webapp/app/js/GEOR.js b/catalogapp/src/main/webapp/app/js/GEOR.js deleted file mode 100644 index 3b61f6b7c2..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR.js +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) Camptocamp - * - * This file is part of geOrchestra - * - * geOrchestra is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with geOrchestra. If not, see . - */ - -/* - * @include GEOR_waiter.js - * @include GEOR_dataview.js - * @include GEOR_csw.js - * @include GEOR_nav.js - * @include GEOR_what.js - * @include GEOR_where.js - * @include GEOR_config.js - */ - -Ext.namespace("GEOR"); - -GEOR.criteria = ['what', 'where']; - -Ext.onReady(function() { - var tr = OpenLayers.i18n; - - /* - * Setting of OpenLayers global vars. - */ - OpenLayers.Lang.setCode(GEOR.config.LANG); - OpenLayers.Number.thousandsSeparator = " "; - OpenLayers.ImgPath = 'app/img/openlayers/'; - OpenLayers.DOTS_PER_INCH = GEOR.config.MAP_DOTS_PER_INCH; - OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3; - - /* - * Setting of Ext global vars. - */ - Ext.BLANK_IMAGE_URL = "lib/externals/ext/resources/images/default/s.gif"; - Ext.apply(Ext.MessageBox.buttonText, { - yes: tr("Yes"), - no: tr("No"), - ok: tr("OK"), - cancel: tr("Cancel") - }); - Ext.QuickTips.init(); - - /* - * Initialize the application. - */ - - GEOR.waiter.init(); - - var store = GEOR.dataview.init(); - - - var o = new Ext.util.Observable(); - o.addEvents( - /** - * Event: searchrequest - * Fires when the server needs to be queried for new results - */ - "searchrequest", - - /** - * Event: storeloaded - * Fires when the new records are loaded in store - */ - "storeloaded", - - /** - * Event: itemselectionchanged - * Fires when list item selection has changed - */ - "itemselectionchanged", - - /** - * Event: itemzoom - * Fires when a user clicks on zoom - */ - "itemzoom" - ); - GEOR.observable = o; - - var whereFilter; - var search = function(options) { - GEOR.waiter.show(); - if (options && options.where) { - whereFilter = options.where; - } - GEOR.dataview.bind(store); - store.load({ - params: { - xmlData: GEOR.csw.getPostData({ - nav: GEOR.nav.getParameters(), - where: whereFilter - }) - } - }); - }; - - /* - * Create the page's layout. - */ - - // the header - var vpItems = GEOR.header ? - [{ - xtype: "box", - id: "geor_header", - region: "north", - height: GEOR.config.HEADER_HEIGHT, - el: "go_head" - }] : []; - - var bbar = new Ext.Toolbar({ - cls: "centerbbar" - }); - - vpItems.push({ - region: "center", - autoScroll: true, - layout: 'fit', - items: [GEOR.dataview.getCmp()], - bbar: bbar - }, { - region: "west", - cls: "westpanel", - width: 280, - minWidth: 280, - maxWidth: 280, - autoScroll: true, - split: true, - collapseMode: "mini", - collapsible: true, - frame: false, - //border: false, - header: false, - defaults: { - collapsible: true, - collapsed: true, - titleCollapse: true, - border: false - }, - items: [Ext.apply(GEOR.what.getCmp(), { - title: tr("Which data are you searching for ?"), - collapsible: false, - collapsed: false, - height: 90 - }), Ext.apply(GEOR.where.getCmp(), { - title: tr("On which area ?"), - collapsed: false, - height: 280 - })/*, { - title: tr("When ?") - }*/], - buttons: [{ - text: tr('clean'), - cls: 'bigbtn', - iconCls: 'geor-btn-reset', - handler: function() { - GEOR.nav.reset(); - var c = GEOR.criteria; - for (var i=0,l=c.length;i 1) { - t = tr('various.results', {'RESULTS': l}) - } else if (l) { - t = tr('one.result') - } - bbar.selText.setText(t); - bbar.selText.getEl().highlight(); - }); - o.on("itemzoom", function(options) { - GEOR.where.zoomTo(options.record); - }); -}); diff --git a/catalogapp/src/main/webapp/app/js/GEOR_Lang/de.js b/catalogapp/src/main/webapp/app/js/GEOR_Lang/de.js deleted file mode 100644 index af0a46aa0e..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR_Lang/de.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -* Copyright (C) Camptocamp -* -* This file is part of geOrchestra -* -* geOrchestra is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with geOrchestra. If not, see . -*/ - -/* -* German translation file -*/ -OpenLayers.Lang.de = OpenLayers.Util.extend(OpenLayers.Lang.de, { - /* General purpose strings */ - "Yes": "Ya", - "No": "Nein", - "OK": "OK", - "Cancel": "Abbrechen", - /* GEOR.js */ - "Which data are you searching for ?": "Welche Angaben suchen Sie ?", - "On which area ?": "Welches Bereich ?", - "When ?": "Wann ?", - "clean": "löschen", - "search": "suchen", - "various.results": "${RESULTS} Metadaten ausgewählt", - "one.result": "1 Metadaten ausgewählt", - /* GEOR_dataview.js strings */ - "View WMS layer": "Anzeigen WMS Layer ${NAME}", - "Download WMS layer": "Download WMS Layer ${NAME}", - "View WMS service": "Anzeigen WMS Service ${NAME}", - "Download WMS service": "Download WMS Service ${NAME}", - "Download data": "Datei ${NAME} downloaden", - "zoom": "zoom", - "record": "Metadata", - "Keywords: ": "Schlüsselwort : ", - "Server NAME": "Rechner ${NAME}", - "Oops, a problem occured.": "Hoppla, es gibt ein Problem.", - /* GEOR_nav.js strings */ - "go to first results": "Zum Resultatanfang gehen", - "previous page": "Vorherige Seite", - "next page": "Nächste Seite", - "go to last results": "Zum Resultatende gehen", - "No result": "Kein Ergebnis", - "Results N1 to N2 of N": "Ergebnis ${N1} zu ${N2} auf ${N}", - "Result N1 to N2 of N": "Ergebnisse ${N1} zu ${N2} auf ${N}", - /* GEOR_what.js strings */ - "enter one or more keywords": "Ein oder mehrere Schlüsselwörter angeben" - // no trailing comma -}); diff --git a/catalogapp/src/main/webapp/app/js/GEOR_Lang/en.js b/catalogapp/src/main/webapp/app/js/GEOR_Lang/en.js deleted file mode 100644 index 85904967fb..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR_Lang/en.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) Camptocamp - * - * This file is part of geOrchestra - * - * geOrchestra is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with geOrchestra. If not, see . - */ - -/* - * English translation file - */ -OpenLayers.Lang.en = OpenLayers.Util.extend(OpenLayers.Lang.en, { - /* GEOR.js */ - "various.results": "${RESULTS} results selected", - /* GEOR_dataview.js strings */ - "View WMS layer": "View the ${NAME} WMS layer", - "Download WMS layer": "Download the ${NAME} WMS layer", - "View WMS service": "View the ${NAME} WMS service", - "Download WMS service": "Download the ${NAME} WMS service", - "Download data": "Download the ${NAME} data", - "Server NAME": "Server ${NAME}", - /* GEOR_nav.js strings */ - "Results N1 to N2 of N": "Results ${N1} to ${N2} of ${N}", - "Result N1 to N2 of N": "Result ${N1} to ${N2} of ${N}" - // no trailing comma -}); diff --git a/catalogapp/src/main/webapp/app/js/GEOR_Lang/es.js b/catalogapp/src/main/webapp/app/js/GEOR_Lang/es.js deleted file mode 100644 index bc54f33a7f..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR_Lang/es.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) Camptocamp - * - * This file is part of geOrchestra - * - * geOrchestra is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with geOrchestra. If not, see . - */ - -/* - * Spanish translation file - */ -OpenLayers.Lang.es = OpenLayers.Util.extend(OpenLayers.Lang.es, { - /* General purpose strings */ - "Yes": "Si", - "No": "No", - "OK": "OK", - "Cancel": "Cancelar", - /* GEOR.js */ - "Which data are you searching for ?": "¿Que datos esta buscando?", - "On which area ?": "¿Sobre que territorio?", - "When ?": "¿Cuando?", - "clean": "borrar", - "search": "buscar", - "various.results": "${RESULTS} fichas seleccionadas", - "one.result": "1 ficha seleccionada", - /* GEOR_dataview.js strings */ - "View WMS layer": "Visualizar la capa WMS ${NAME}", - "Download WMS layer": "Descargar la capa WMS ${NAME}", - "View WMS service": "Visualizar el servicio WMS ${NAME}", - "Download WMS service": "Descargar el servicio WMS ${NAME}", - "Download data": "Descargar el dato ${NAME}", - "zoom": "extensión espacial", - "record": "ficha", - "Keywords: ": "Palabras clave: ", - "Server NAME": "Servidor ${NAME}", - "Oops, a problem occured.": "Oops, occurrió un problema.", - /* GEOR_nav.js strings */ - "go to first results": "ir a los primeros resultados", - "previous page": "página anterior", - "next page": "página siguiente", - "go to last results": "ir a los últimos resultados", - "No result": "Ningún resultado", - "Results N1 to N2 of N": "Resultados ${N1} a ${N2} de ${N}", - "Result N1 to N2 of N": "Resultado ${N1} a ${N2} de ${N}", - /* GEOR_what.js strings */ - "enter one or more keywords": "ingresar una o varias palabras clave" - // no trailing comma -}); diff --git a/catalogapp/src/main/webapp/app/js/GEOR_Lang/fr.js b/catalogapp/src/main/webapp/app/js/GEOR_Lang/fr.js deleted file mode 100644 index 1f35e24897..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR_Lang/fr.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) Camptocamp - * - * This file is part of geOrchestra - * - * geOrchestra is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with geOrchestra. If not, see . - */ - -/* - * French translation file - */ -OpenLayers.Lang.fr = OpenLayers.Util.extend(OpenLayers.Lang.fr, { - /* General purpose strings */ - "Yes": "Oui", - "No": "Non", - "OK": "OK", - "Cancel": "Annuler", - /* GEOR.js */ - "Which data are you searching for ?": "Quelles données cherchez vous ?", - "On which area ?": "Sur quel territoire ?", - "When ?": "Quand ?", - "clean": "effacer", - "search": "chercher", - "various.results": "${RESULTS} fiches sélectionnées", - "one.result": "1 fiche sélectionnée", - /* GEOR_dataview.js strings */ - "View WMS layer": "Visualiser la couche WMS ${NAME}", - "Download WMS layer": "Télécharger la couche WMS ${NAME}", - "View WMS service": "Visualiser le service WMS ${NAME}", - "Download WMS service": "Télécharger le service WMS ${NAME}", - "Download data": "Télécharger la donnée ${NAME}", - "zoom": "zoom", - "record": "fiche", - "Keywords: ": "Mots clés : ", - "Server NAME": "Serveur ${NAME}", - "Oops, a problem occured.": "Oops, il y a eu un problème.", - /* GEOR_nav.js strings */ - "go to first results": "aller au début des résultats", - "previous page": "page précédente", - "next page": "page suivante", - "go to last results": "aller à la fin des résultats", - "No result": "Aucun résultat", - "Results N1 to N2 of N": "Résultats ${N1} à ${N2} sur ${N}", - "Result N1 to N2 of N": "Résultat ${N1} à ${N2} sur ${N}", - /* GEOR_what.js strings */ - "enter one or more keywords": "saisissez un ou plusieurs mots clés" - // no trailing comma -}); diff --git a/catalogapp/src/main/webapp/app/js/GEOR_config.js b/catalogapp/src/main/webapp/app/js/GEOR_config.js deleted file mode 100644 index c2aeba3dcb..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR_config.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) Camptocamp - * - * This file is part of geOrchestra - * - * geOrchestra is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with geOrchestra. If not, see . - */ - -Ext.namespace("GEOR"); - -GEOR.config = (function() { - - - /** - * Method: getCustomParameter - * If parameter paramName exists in GEOR.custom, returns its value - * else defaults to the mandatory defaultValue - * - * Parameters: - * paramName - {String} the parameter name - * defaultValue - {Mixed} the default value if none is - * specified in GEOR.custom - * - * Returns: - * {Mixed} The parameter value - */ - var getCustomParameter = function(paramName, defaultValue) { - return (GEOR.custom && GEOR.custom.hasOwnProperty(paramName)) ? - GEOR.custom[paramName] : defaultValue; - }; - - return { - /** - * Constant: HEADER_HEIGHT - * Integer value representing the header height, as set in the shared maven filters - * Defaults to 90 - */ - HEADER_HEIGHT: getCustomParameter("HEADER_HEIGHT", 90), - - /***** Beginning of config options which can be overriden by GEOR.custom *****/ - - /** - * Constant: GEONETWORK_URL - * The URL to the GeoNetwork server. - * Defaults to "/geonetwork/srv/fre" - */ - GEONETWORK_URL: getCustomParameter('GEONETWORK_URL', - "/geonetwork/srv/fre"), - - /** - * Constant: VIEWER_URL - * The URL to Mapfishapp - * Defaults to "/mapfishapp/" - */ - VIEWER_URL: getCustomParameter('VIEWER_URL', - "/mapfishapp/"), - - /** - * Constant: EXTRACTOR_URL - * The URL to Extractorapp - * Defaults to "/extractorapp/" - */ - EXTRACTOR_URL: getCustomParameter('EXTRACTOR_URL', - "/extractorapp/"), - - /** - * Constant: MAP_DOTS_PER_INCH - * {Float} Sets the resolution used for scale computation. - * Defaults to GeoServer defaults, which is 25.4 / 0.28 - */ - MAP_DOTS_PER_INCH: getCustomParameter('MAP_DOTS_PER_INCH', - 25.4 / 0.28), - - RESULTS_PER_PAGE: getCustomParameter('RESULTS_PER_PAGE', - 20) - }; -})(); diff --git a/catalogapp/src/main/webapp/app/js/GEOR_csw.js b/catalogapp/src/main/webapp/app/js/GEOR_csw.js deleted file mode 100644 index 3016602526..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR_csw.js +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) Camptocamp - * - * This file is part of geOrchestra - * - * geOrchestra is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with geOrchestra. If not, see . - */ - - -/* - * @include OpenLayers/Filter/Logical.js - * @include OpenLayers/Format/CSWGetRecords.js - * @include OpenLayers/Format/CSWGetRecords/v2_0_2.js - */ - -Ext.namespace("GEOR"); - -GEOR.csw = (function() { - - var format = null; - - var getFilter = function(options) { - var f, filters = [], criteria = GEOR.criteria; - - for (var i=0, l = criteria.length; i= 1) { - return new OpenLayers.Filter.Logical({ - type: '&&', - filters: filters - }); - } - return null; - }; - - return { - - getPostData: function(options) { - format = format || new OpenLayers.Format.CSWGetRecords(); - - var query = { - ElementSetName: { - value: "full" - }/*, - SortBy: // TODO. - */ - }; - - var filter = getFilter(options); - if (filter) { - query.Constraint = { - version: "1.1.0", - Filter: filter - }; - } - - return format.write({ - resultType: "results_with_summary", - Query: query, - startPosition: options && options.nav && options.nav.startPosition || 1, - maxRecords: options && options.nav && options.nav.maxResults || 999 - /*, - outputSchema: options.filter && "http://www.isotc211.org/2005/gmd" // to get ISO19139 XML when a filter is specified (~ getRecordById) - */ - }); - } - - }; -})(); diff --git a/catalogapp/src/main/webapp/app/js/GEOR_dataview.js b/catalogapp/src/main/webapp/app/js/GEOR_dataview.js deleted file mode 100644 index f6ec311695..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR_dataview.js +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (C) Camptocamp - * - * This file is part of geOrchestra - * - * geOrchestra is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with geOrchestra. If not, see . - */ - -/* - * @include GeoExt.data.CSW.js - * @include GEOR_config.js - * @include GEOR_waiter.js - * @include OpenLayers/Format/JSON.js - */ - -Ext.namespace("GEOR"); - -GEOR.dataview = (function() { - - var tr = OpenLayers.i18n; - - var store = null; - - var dataView = null; - - var OWSdb = {}; - - var form = {}, jsonFormat; - - var selectedRecordsId = []; - - var createButtons = function(URIs) { - if (!URIs || !URIs[0]) { - return ''; - } - var id, dl = [], view = [], URI; - for (var i=0,l=URIs.length;i' - +tr('View WMS layer', {NAME: (URI.description || URI.name)}) - +''); - dl.push(''); - } else { - // we have a service - view.push(''); - dl.push(''); - } - } - break; - /* - case '': - case 'image/png': - case 'WWW:DOWNLOAD-1.0-http--download': - if (URI[i].value) { - OWSdb[id] = URI[i]; - dl.push(''); - } - break; - */ - } - } - return dl.join(' ')+view.join(' '); - }; - - var getZoomText = function(values) { - var bbox = values.BoundingBox; - var uuid = values.identifier; - return (bbox instanceof OpenLayers.Bounds ? ' - ' + tr('zoom') + '' : ''); - }; - - - var getTemplate = function() { - return [ - '', - '
', - '

{title} - ' + tr('record') + '', - '{[this.zoom(values)]}

', - '

{abstract}

', - '{[this.buttons(values.URI)]}', - /*tr('Keywords: '), - '', - ' {value} ', - '',*/ - '
', - '
' - ].join(''); - }; - - var submitData = function(url_key, o) { - form[url_key] = form[url_key] || Ext.DomHelper.append(Ext.getBody(), { - tag: "form", - action: GEOR.config[url_key], //url_key can be one of VIEWER_URL or EXTRACTOR_URL - target: "_blank", - method: "post" - }); - var input = form[url_key][0] || Ext.DomHelper.append(form[url_key], { - tag: "input", - type: "hidden", - name: "data" - }); - jsonFormat = jsonFormat || new OpenLayers.Format.JSON(); - input.value = jsonFormat.write(o); - form[url_key].submit(); - }; - - - var onButtonClick = function(evt, elt) { - elt = Ext.get(elt); - if (!elt.is('button')) { - elt = elt.parent('button'); - } - if (!OWSdb[elt.id]) { - return; - } - var url_key = (elt.hasClass('x-list-btn-view')) ? - 'VIEWER_URL' : 'EXTRACTOR_URL'; - var services = [], layers = []; - if (OWSdb[elt.id].name) { - layers.push({ - layername: OWSdb[elt.id].name, - metadataURL:"", // FIXME - owstype:"WMS", // What about WFS ? - owsurl: OWSdb[elt.id].value - }); - } else { - services.push({ - text: tr("Server NAME", {'NAME': OWSdb[elt.id].value}), - metadataURL:"", // FIXME - owstype:"WMS", - owsurl: OWSdb[elt.id].value - }); - } - submitData(url_key, {services: services, layers: layers}); - }; - - var getRecordFromHref = function(href) { - var uuid = href.slice(href.indexOf('#')+1); - return store.getById(uuid); - }; - - var onZoomClick = function(e, t) { - // TODO: change event name to zoom - var r = getRecordFromHref(t.href); - if (r) { - GEOR.observable.fireEvent("itemzoom", { - record: r - }); - } - }; - - var onStoreLoad = function(s) { - Ext.select('button.x-list-btn-view').on('click', onButtonClick); - Ext.select('button.x-list-btn-dl').on('click', onButtonClick); - Ext.select('.x-view-item a.zoom').on('click', onZoomClick); - GEOR.waiter.hide(); - // we need to restore selection of items referenced in selectedRecords - if (selectedRecordsId.length) { - dataView.select(store.queryBy(function(r, id) { - return (selectedRecordsId.indexOf(id) > -1); - }).getRange(), true, true); - } - GEOR.observable.fireEvent("storeloaded", {store: s}); - }; - - - var onStoreBeforeload = function() { - // local db reset - OWSdb = {}; - }; - - var onStoreException = function() { - GEOR.waiter.hide(); - alert(tr("Oops, a problem occured.")); - }; - - - return { - - init: function(s) { - if (!store) { - store = new GeoExt.data.CSWRecordsStore({ - url: GEOR.config.GEONETWORK_URL + '/csw', //'content.xml', // - listeners: { - "beforeload": onStoreBeforeload, - "load": onStoreLoad, - "exception": onStoreException - } - }); - } - return store; - }, - - bind: function(store) { - if (!dataView.store) { - dataView.bindStore(store, true); - } - }, - - - getCmp: function() { - if (!dataView) { - dataView = new Ext.DataView({ - //store: store, // do not specify store right now, or contentEl will be overwritten. - singleSelect: null, - //multiSelect: true, - selectedClass: 'x-view-selected', - //simpleSelect: true, - cls: 'x-list', - overClass:'x-view-over', - itemSelector: 'div.x-view-item', - autoScroll: true, - autoWidth: true, - contentEl: "dataview-contentel", - //trackOver: true, - autoHeight: true, - tpl: new Ext.XTemplate(getTemplate(), { - buttons: createButtons, - zoom: getZoomText - }), - listeners: { - "click": function(dv, idx, node) { - var selectedRecords = dv.getSelectedRecords(); - var length = selectedRecords.length; - selectedRecordsId = new Array(length); - for (var i=0,l=length;i. - */ - -Ext.namespace("GEOR"); - -GEOR.nav = (function() { - - /* - * Private - */ - var tr = OpenLayers.i18n; - var ok; - var startPosition = 1; - var numberOfRecordsMatched; - var RESULTSPERPAGE = GEOR.config.RESULTS_PER_PAGE; - - var isMax = function() { - return (startPosition == Math.floor((numberOfRecordsMatched - 1)/RESULTSPERPAGE) * RESULTSPERPAGE + 1); - }; - - var handleNavigation = function(store, bar) { - numberOfRecordsMatched = store.getTotalCount(); - - var max = isMax() ? numberOfRecordsMatched : parseInt(startPosition + RESULTSPERPAGE - 1); - - if (bar.items.length === 0) { - bar.add({ - text: '<<', - ref: 'first', - disabled: true, - handler: GEOR.nav.begin, - tooltip: tr("go to first results"), - width: 30 - },{ - text: '<', - ref: 'previous', - disabled: true, - handler: GEOR.nav.previousPage, - tooltip: tr("previous page"), - width: 30 - },{ - text: '>', - ref: 'next', - handler: GEOR.nav.nextPage, - tooltip: tr("next page"), - width: 30 - },{ - text: '>>', - ref: 'end', - handler: GEOR.nav.end, - tooltip: tr("go to last results"), - width: 30 - }, new Ext.Toolbar.TextItem({ - text: '', - ref: 'navText' - }), '->', new Ext.Toolbar.TextItem({ - text: '', - ref: 'selText' - })); - bar.ownerCt.doLayout(); - } - - if (numberOfRecordsMatched == 0) { - bar.first.disable(); - bar.previous.disable(); - bar.next.disable(); - bar.end.disable(); - bar.navText.setText(tr("No result")); - } else { - if (numberOfRecordsMatched > 1) { - bar.navText.setText(tr("Results N1 to N2 of N", { - 'N1': startPosition, - 'N2': max, - 'N': numberOfRecordsMatched - })); - } else { - bar.navText.setText(tr("Result N1 to N2 of N", { - 'N1': startPosition, - 'N2': max, - 'N': numberOfRecordsMatched - })); - } - // handle buttons activation/deactivation: - bar.first.setDisabled(startPosition == 1); - bar.previous.setDisabled(startPosition == 1); - bar.next.setDisabled(isMax()); - bar.end.setDisabled(isMax()); - } - }; - - /* - * Public - */ - return { - - getParameters: function(options) { - return { - "startPosition": startPosition, - "maxResults": startPosition + RESULTSPERPAGE - 1 - }; - }, - - update: handleNavigation, - - reset: function() { - startPosition = 1; - }, - - nextPage: function() { - if (isMax()) { - return; - } - startPosition += RESULTSPERPAGE; - GEOR.observable.fireEvent("searchrequest"); - }, - - previousPage: function() { - if (startPosition == 1) { - return; - } - startPosition -= RESULTSPERPAGE; - GEOR.observable.fireEvent("searchrequest"); - }, - - begin: function() { - if (startPosition == 1) { - return; - } - startPosition = 1; - GEOR.observable.fireEvent("searchrequest"); - }, - - end: function() { - if (isMax()) { - return; - } - startPosition = Math.floor((numberOfRecordsMatched - 1)/RESULTSPERPAGE) * RESULTSPERPAGE + 1; - GEOR.observable.fireEvent("searchrequest"); - } - }; -})(); \ No newline at end of file diff --git a/catalogapp/src/main/webapp/app/js/GEOR_waiter.js b/catalogapp/src/main/webapp/app/js/GEOR_waiter.js deleted file mode 100644 index 351e5fa71f..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR_waiter.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) Camptocamp - * - * This file is part of geOrchestra - * - * geOrchestra is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with geOrchestra. If not, see . - */ - -Ext.namespace("GEOR"); - -GEOR.waiter = (function() { - /* - * Private - */ - - var waiter = null; - - return { - - init: function() { - waiter = Ext.get('waiter'); - }, - - hide: function() { - if (waiter && waiter.isVisible()) { - waiter.hide(); - } - }, - - show: function(c) { - if (waiter && !waiter.isVisible()) { - waiter.show(); - } - } - }; -})(); diff --git a/catalogapp/src/main/webapp/app/js/GEOR_what.js b/catalogapp/src/main/webapp/app/js/GEOR_what.js deleted file mode 100644 index 70eee627d6..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR_what.js +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) Camptocamp - * - * This file is part of geOrchestra - * - * geOrchestra is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with geOrchestra. If not, see . - */ - -/* - * @include OpenLayers/Filter/Comparison.js - */ - -Ext.namespace("GEOR"); - -GEOR.what = (function() { - - var tr = OpenLayers.i18n; - - var field; - - return { - - getCmp: function() { - if (field) { - return; - } - field = new Ext.form.TextField({ - label: ' ', - width: 210, - emptyText: tr('enter one or more keywords'), - name: 'what', - enableKeyEvents: true, - listeners: { - "specialkey": function(field, e){ - if (e.getKey() == e.ENTER) { - GEOR.observable.fireEvent("searchrequest", { - reset: true - }); - } - } - } - }); - - return { - xtype: 'form', - labelSeparator: ' ', - labelWidth: 1, - bodyStyle: 'padding: 20px;', - items: [field] - }; - }, - - getFilter: function() { - var v = field && field.getValue(); - if (v) { - return new OpenLayers.Filter.Comparison({ - type: "~", - // OL (format/filter/1.1.0) has wildCard: "*", singleChar: ".", escapeChar: "!" hardcoded - property: "AnyText", - value: '*'+v+'*' - }); - } - return null; - }, - - reset: function() { - field && field.reset(); - } - }; -})(); diff --git a/catalogapp/src/main/webapp/app/js/GEOR_where.js b/catalogapp/src/main/webapp/app/js/GEOR_where.js deleted file mode 100644 index 8c8825f4c7..0000000000 --- a/catalogapp/src/main/webapp/app/js/GEOR_where.js +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) Camptocamp - * - * This file is part of geOrchestra - * - * geOrchestra is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with geOrchestra. If not, see . - */ - -/* - * @include OpenLayers/Map.js - * @include OpenLayers/Control/Navigation.js - * @include OpenLayers/Control/Attribution.js - * @include OpenLayers/Filter/Spatial.js - * @include OpenLayers/Feature/Vector.js - * @include OpenLayers/Projection.js - * @include OpenLayers/Layer/SphericalMercator.js - * @include OpenLayers/Layer/XYZ.js - * @include OpenLayers/Layer/Vector.js - * @include OpenLayers/StyleMap.js - * @include OpenLayers/Style.js - * @include OpenLayers/Control/DrawFeature.js - * @include OpenLayers/Handler/RegularPolygon.js - * @include OpenLayers/Renderer/SVG.js - * @include OpenLayers/Renderer/VML.js - * @include GeoExt/widgets/MapPanel.js - */ - -Ext.namespace("GEOR"); - -GEOR.where = (function() { - - var map; - var extentLayer, vectorLayer; - var epsg4326, epsg900913; - var searchExtent; - - - var createVectorLayer = function() { - vectorLayer = new OpenLayers.Layer.Vector('bounds', { - styleMap: new OpenLayers.StyleMap({ - "default": new OpenLayers.Style({ - fillOpacity: 0, - strokeColor: "#ee9900", - strokeWidth: 2, - strokeOpacity: 0.4 - }), - "select": new OpenLayers.Style({ - fillOpacity: 0, - strokeColor: "blue", - strokeWidth: 2, - strokeOpacity: 1, - graphicZIndex: 1000 - }) - }), - rendererOptions: { - zIndexing: true - }, - getFeatureByAttributeId: function(value) { - var feature = null; - for(var i=0, len=this.features.length; iOpenStreetMap" - }) - ], - eventListeners: { - "moveend": onMapMoveend - } - }); - map.zoomToMaxExtent(); - // the layer which will display the search extent: - extentLayer = new OpenLayers.Layer.Vector('searchextent', { - styleMap: new OpenLayers.StyleMap({ - "default": new OpenLayers.Style({ - fillColor: "#000000", - fillOpacity: 0, - strokeColor: "#ff0000", - strokeDashstyle: "dash", - strokeWidth: 2, - strokeOpacity: 1 - }) - }) - }); - map.addLayer(extentLayer); - return map; - }; - - return { - - getCmp: function() { - if (map) { - return Ext.getCmp('geor-map'); - } - map = createMap(); - return { - xtype: "gx_mappanel", - id: 'geor-map', - stateful: false, - map: map - }; - }, - - // TODO: inclusion stricte ? - getFilter: function() { - var filter = null; - if (!epsg4326) { - epsg4326 = new OpenLayers.Projection("EPSG:4326"); - } - var bounds = map.getExtent(); - // when getFilter is called, we want to keep a state with the bbox being searched: - searchExtent = bounds.clone(); - filter = new OpenLayers.Filter.Spatial({ - type: OpenLayers.Filter.Spatial.BBOX, - property: "ows:BoundingBox", - value: bounds.clone().transform(epsg900913, epsg4326) - }); - return filter; - }, - - display: function(records) { - if (!vectorLayer) { - createVectorLayer(); - } else { - vectorLayer.destroyFeatures(); - } - - var record, bounds, features = [], geom; - for (var i=0, l=records.length; i. - */ - -/* - * @include OpenLayers/Format/CSWGetRecords/v2_0_2.js - * @requires GeoExt/data/LayerRecord.js - * FIXME: - * @include OpenLayers/Request.js - * @include OpenLayers/Request/XMLHttpRequest.js - */ - - -/////////////////// GeoExt.data.CSWRecordsReader -Ext.namespace("GeoExt.data"); -GeoExt.data.CSWRecordsReader = function(meta, recordType) { - meta = meta || {}; - if(!meta.format) { - meta.format = new OpenLayers.Format.CSWGetRecords(); - } - if(typeof recordType !== "function") { - recordType = GeoExt.data.LayerRecord.create( - recordType || meta.fields || [ - {name: "audience"}, - {name: "contributor"}, - {name: "coverage"}, - {name: "creator"}, - {name: "date"}, - {name: "description"}, - {name: "format"}, - {name: "identifier"}, - {name: "language"}, - {name: "provenance"}, - {name: "publisher"}, - {name: "relation"}, - {name: "rights"}, - {name: "rightsHolder"}, - {name: "source"}, - {name: "subject"}, - {name: "title"}, - {name: "type"}, - {name: "URI"}, - {name: "abstract"}, - {name: "modified"}, - {name: "spatial"}, - {name: "BoundingBox", mapping: "bounds"} - ] - ); - } - GeoExt.data.CSWRecordsReader.superclass.constructor.call( - this, meta, recordType - ); -}; - -Ext.extend(GeoExt.data.CSWRecordsReader, Ext.data.DataReader, { - /** private: method[read] - * :param request: ``Object`` The XHR object which contains the parsed XML - * document. - * :return: ``Object`` A data block which is used by an ``Ext.data.Store`` - * as a cache of ``Ext.data.Record`` objects. - */ - read: function(request) { - var data = request.responseXML; - if(!data || !data.documentElement) { - data = request.responseText; - } - return this.readRecords(data); - }, - - readers: { - "title": function(r) { - var o = []; - for (var i=0, l=r.length; i - - Ext.ux Tree Components - - - - - - - - - - - -

Tree Store Node Example

-

This is example that shows how create a TreePanel with store - nodes.

- -

Note that the js is not minified so it is readable. - See treestorenode.js.

- -
-
-
- - diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/examples/treestorenode.js b/catalogapp/src/main/webapp/lib/Ext.ux/examples/treestorenode.js deleted file mode 100644 index f96e085157..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/examples/treestorenode.js +++ /dev/null @@ -1,67 +0,0 @@ -Ext.onReady(function() { - - var states = [ - ['AL', 'Alabama', 'The Heart of Dixie'], - ['AK', 'Alaska', 'The Land of the Midnight Sun'], - ['AZ', 'Arizona', 'The Grand Canyon State'], - ['AR', 'Arkansas', 'The Natural State'], - ['CA', 'California', 'The Golden State'], - ['CO', 'Colorado', 'The Mountain State'] - ]; - - // simple array store - var store = new Ext.data.SimpleStore({ - fields: ['abbr', 'state', 'text', {name: 'displayInTree', defaultValue: true}], - data : states - }); - - - var tree = new Ext.tree.TreePanel({ - width:220, - height:300, - rootVisible: false, - root: new Ext.tree.AsyncTreeNode({ - expanded: true, - text: 'invisible root node', - leaf: false, - children: [{ - text: 'dummy normal node', - expanded: true, - children: [{ - text: 'dummy normal leaf', - leaf: true - }] - }, - { - nodeType: 'ux_treestorenode', - loaded: true, - leaf: false, - expanded: true, - text: 'TreeStoreNode', - store: store, - defaults: { - checked: false - } - } - ] - }) - }); - - // the node's check button with its checkchange event can be used - // to call a custom handler. - var registerCheckbox = function(node){ - if(!node.hasListener("checkchange")) { - node.on("checkchange", function(node, checked){ - alert(node.record.get('text') + " has been " + (checked==true?"":"un") + "checked."); - }); - } - } - tree.on({ - "insert": registerCheckbox, - "append": registerCheckbox, - scope: this - }); - - tree.render(Ext.get('tree')); - -}); diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux.js deleted file mode 100644 index fa26910dab..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux.js +++ /dev/null @@ -1,75 +0,0 @@ -/* - * The code in this file is based on code taken from OpenLayers. - * - * Copyright (c) 2006-2007 MetaCarta, Inc., published under the Clear BSD - * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the - * full text of the license. - */ - -(function() { - - /** - * Check to see if Ext.ux.singleFile is true. It is true if the - * Ext.ux/SingleFile.js is included before this one, as it is - * the case in single file builds. - */ - var singleFile = (typeof Ext.ux == "object" && Ext.ux.singleFile); - - /** - * The relative path of this script. - */ - var scriptName = singleFile ? "Ext.ux.js" : "lib/Ext.ux.js"; - - /** - * Function returning the path of this script. - */ - var getScriptLocation = function() { - var scriptLocation = ""; - var scripts = document.getElementsByTagName('script'); - for(var i=0, len=scripts.length; i -1) && (index + scriptName.length == pathLength)) { - scriptLocation = src.slice(0, pathLength - scriptName.length); - break; - } - } - } - return scriptLocation; - }; - - /** - * If Ext.ux.singleFile is false then the JavaScript files in the jsfiles - * array are autoloaded. - */ - if(!singleFile) { - var jsfiles = new Array( - "Ext.ux/widgets/tree/TreeRecordNode.js", - "Ext.ux/widgets/tree/TreeStoreNode.js", - "Ext.ux/widgets/tree/XmlTreeLoader.js", - "Ext.ux/widgets/spinner/Spinner.js", - "Ext.ux/widgets/spinner/SpinnerStrategy.js", - "Ext.ux/widgets/spinner/NumberSpinner.js", - "Ext.ux/widgets/colorpicker/ColorPicker.js", - "Ext.ux/widgets/colorpicker/ColorMenu.js", - "Ext.ux/widgets/colorpicker/ColorPickerField.js", - "Ext.ux/widgets/palettecombobox/PaletteComboBox.js" - ); - - var agent = navigator.userAgent; - var allScriptTags = new Array(jsfiles.length); - var host = getScriptLocation() + "lib/"; - for (var i=0, len=jsfiles.length; i"; - } - document.write(allScriptTags.join("")); - } -})(); diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/SingleFile.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/SingleFile.js deleted file mode 100644 index 82c1c7ffaf..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/SingleFile.js +++ /dev/null @@ -1,3 +0,0 @@ -Ext.namespace("Ext.ux"); - -Ext.ux.singleFile = true; diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/ColorMenu.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/ColorMenu.js deleted file mode 100644 index dceb98e9bf..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/ColorMenu.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @class Ext.ux.menu.ColorMenu - * @extends Ext.menu.Menu - * This class makes Ext.ux.ColorPicker available as a menu. - * @license: BSD - * @author: Robert B. Williams (extjs id: vtswingkid) - * @author: Tobias Uhlig (extjs id: tobiu) - * @author: Jerome Carbou (extjs id: jcarbou) - * @constructor - * Creates a new ColorMenu - * @param {Object} config Configuration options - * @version 1.1.3 - */ - -Ext.namespace("Ext.ux.menu", "Ext.ux.form"); - -Ext.ux.menu.ColorMenu = Ext.extend(Ext.menu.Menu, { - enableScrolling : false, - hideOnClick : true, - initComponent : function(){ - Ext.apply(this, { - plain : true, - showSeparator : false, - items: this.picker = new Ext.ux.ColorPicker(this.initialConfig) - }); - Ext.ux.menu.ColorMenu.superclass.initComponent.call(this); - this.relayEvents(this.picker, ['select']); - this.on('select', this.menuHide, this); - if (this.handler) { - this.on('select', this.handler, this.scope || this) - } - }, - menuHide: function(){ - if (this.hideOnClick) { - this.hide(true); - } - } -}); diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/ColorPicker.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/ColorPicker.js deleted file mode 100644 index cd39165b16..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/ColorPicker.js +++ /dev/null @@ -1,884 +0,0 @@ -/** - * @class Ext.ux.ColorPicker - * @extends Ext.BoxComponent - * This is a color picker. - * @license: LGPLv3 - * @author: Amon - * @author: Jerome Carbou (extjs id: jcarbou) - * @constructor - * Creates a new ColorPicker - * @param {Object} config Configuration options - * @version 1.1.3 - */ - -Ext.namespace( 'Ext.ux' ); -/** - * - */ -Ext.ux.ColorPicker = Ext.extend( Ext.BoxComponent, { - opacity:true, - - colorNames : { - "aliceblue": "F0F8FF", - "antiquewhite": "FAEBD7", - "aqua": "00FFFF", - "aquamarine": "7FFFD4", - "azure": "F0FFFF", - "beige": "F5F5DC", - "bisque": "FFE4C4", - "black": "000000", - "blanchedalmond": "FFEBCD", - "blue": "0000FF", - "blueviolet": "8A2BE2", - "brown": "A52A2A", - "burlywood": "DEB887", - "cadetblue": "5F9EA0", - "chartreuse": "7FFF00", - "chocolate": "D2691E", - "coral": "FF7F50", - "cornflowerblue": "6495ED", - "cornsilk": "FFF8DC", - "crimson": "DC143C", - "cyan": "00FFFF", - "darkblue": "00008B", - "darkcyan": "008B8B", - "darkgoldenrod": "B8860B", - "darkgray": "A9A9A9", - "darkgreen": "006400", - "darkgrey": "A9A9A9", - "darkkhaki": "BDB76B", - "darkmagenta": "8B008B", - "darkolivegreen": "556B2F", - "darkorange": "FF8C00", - "darkorchid": "9932CC", - "darkred": "8B0000", - "darksalmon": "E9967A", - "darkseagreen": "8FBC8F", - "darkslateblue": "483D8B", - "darkslategray": "2F4F4F", - "darkslategrey": "2F4F4F", - "darkturquoise": "00CED1", - "darkviolet": "9400D3", - "deeppink": "FF1493", - "deepskyblue": "00BFFF", - "dimgray": "696969", - "dimgrey": "696969", - "dodgerblue": "1E90FF", - "firebrick": "B22222", - "floralwhite": "FFFAF0", - "forestgreen": "228B22", - "fuchsia": "FF00FF", - "gainsboro": "DCDCDC", - "ghostwhite": "F8F8FF", - "gold": "FFD700", - "goldenrod": "DAA520", - "gray": "808080", - "green": "008000", - "greenyellow": "ADFF2F", - "grey": "808080", - "honeydew": "F0FFF0", - "hotpink": "FF69B4", - "indianred": "CD5C5C", - "indigo": "4B0082", - "ivory": "FFFFF0", - "khaki": "F0E68C", - "lavender": "E6E6FA", - "lavenderblush": "FFF0F5", - "lawngreen": "7CFC00", - "lemonchiffon": "FFFACD", - "lightblue": "ADD8E6", - "lightcoral": "F08080", - "lightcyan": "E0FFFF", - "lightgoldenrodyellow": "FAFAD2", - "lightgray": "D3D3D3", - "lightgreen": "90EE90", - "lightgrey": "D3D3D3", - "lightpink": "FFB6C1", - "lightsalmon": "FFA07A", - "lightseagreen": "20B2AA", - "lightskyblue": "87CEFA", - "lightslategray": "778899", - "lightslategrey": "778899", - "lightsteelblue": "B0C4DE", - "lightyellow": "FFFFE0", - "lime": "00FF00", - "limegreen": "32CD32", - "linen": "FAF0E6", - "magenta": "FF00FF", - "maroon": "800000", - "mediumaquamarine": "66CDAA", - "mediumblue": "0000CD", - "mediumorchid": "BA55D3", - "mediumpurple": "9370DB", - "mediumseagreen": "3CB371", - "mediumslateblue": "7B68EE", - "mediumspringgreen": "00FA9A", - "mediumturquoise": "48D1CC", - "mediumvioletred": "C71585", - "midnightblue": "191970", - "mintcream": "F5FFFA", - "mistyrose": "FFE4E1", - "moccasin": "FFE4B5", - "navajowhite": "FFDEAD", - "navy": "000080", - "oldlace": "FDF5E6", - "olive": "808000", - "olivedrab": "6B8E23", - "orange": "FFA500", - "orangered": "FF4500", - "orchid": "DA70D6", - "palegoldenrod": "EEE8AA", - "palegreen": "98FB98", - "paleturquoise": "AFEEEE", - "palevioletred": "DB7093", - "papayawhip": "FFEFD5", - "peachpuff": "FFDAB9", - "peru": "CD853F", - "pink": "FFC0CB", - "plum": "DDA0DD", - "powderblue": "B0E0E6", - "purple": "800080", - "red": "FF0000", - "rosybrown": "BC8F8F", - "royalblue": "4169E1", - "saddlebrown": "8B4513", - "salmon": "FA8072", - "sandybrown": "F4A460", - "seagreen": "2E8B57", - "seashell": "FFF5EE", - "sienna": "A0522D", - "silver": "C0C0C0", - "skyblue": "87CEEB", - "slateblue": "6A5ACD", - "slategray": "708090", - "slategrey": "708090", - "snow": "FFFAFA", - "springgreen": "00FF7F", - "steelblue": "4682B4", - "tan": "D2B48C", - "teal": "008080", - "thistle": "D8BFD8", - "tomato": "FF6347", - "turquoise": "40E0D0", - "violet": "EE82EE", - "wheat": "F5DEB3", - "white": "FFFFFF", - "whitesmoke": "F5F5F5", - "yellow": "FFFF00", - "yellowgreen": "9ACD32" - }, - - /** - * - */ - initComponent: function() { - this.applyDefaultsCP(); - Ext.ux.ColorPicker.superclass.initComponent.apply( this, arguments ); - this.addEvents('select'); - }, - /** - * - */ - onRender: function() { - Ext.ux.ColorPicker.superclass.onRender.apply( this, arguments ); - // check if container, self-container or renderTo exists - this.body = this.body || ( this.container || ( this.renderTo || Ext.DomHelper.append( Ext.getBody(), {}, true ) ) ); - if( !this.el ) { - this.el = this.body; - if( this.cls ) { Ext.get( this.el ).addClass( this.cls ); } - } - this.body.setSize(328,this.opacity ? 211 : 191); - // render this component - this.renderComponent(); - }, - /** - * - */ - applyDefaultsCP: function() { - Ext.apply( this, { - 'cls': 'x-cp-mainpanel', - 'resizable': this.resizable || false, - 'HSV': { - h: 0, - s: 0, - v: 0 - }, - updateMode: null - }); - }, - /** - * - */ - renderComponent: function() { - // create RGB Picker - this.rgbEl = Ext.DomHelper.append( this.body, { - 'id': this.cpGetId( 'rgb' ), - 'cls': 'x-cp-rgbpicker' - },true); - // Create HUE Picker - this.hueEl = Ext.DomHelper.append( this.body, { - 'id': this.cpGetId( 'hue' ), - 'cls': 'x-cp-huepicker' - },true); - // Create Opacity Picker - if (this.opacity) { - this.opacityEl = Ext.DomHelper.append( this.body, { - 'id': this.cpGetId( 'opacity' ), - 'cls': 'x-cp-opacitypicker' - },true); - } - // Initialize HUE Picker DD - this.hueSliderEl = Ext.DomHelper.append( this.body, { 'cls': 'x-cp-slider',id:this.cpGetId( 'hueSlider' ) },true); - this.hueDD = new Ext.dd.DD( this.hueSliderEl, 'huePicker' ); - this.hueDD.constrainTo( this.hueEl.id, {'top':-7,'right':-3,'bottom':-7,'left':-3} ); - this.hueDD.onDrag = this.moveHuePicker.createDelegate( this ); - // initialize onclick on the rgb picker - this.hueEl.on( 'mousedown', this.clickHUEPicker.createDelegate( this ) ); - // initialize start position - Ext.get( this.hueSliderEl ).moveTo( this.hueEl.getLeft() - 3, this.hueEl.getTop() - 7 ); - - // Initialize RGB Picker DD - this.rgbSliderEl = Ext.DomHelper.append( this.body, { 'cls': 'x-cp-slider',id:this.cpGetId( 'rgbSlider' ) },true); - this.rgbDD = new Ext.dd.DD( this.rgbSliderEl.id, 'rgbPicker' ); - this.rgbDD.constrainTo( this.rgbEl.id, -7 ); - this.rgbDD.onDrag = this.moveRGBPicker.createDelegate( this ); - // initialize onclick on the rgb picker - this.rgbEl.on( 'mousedown', this.clickRGBPicker.createDelegate( this ) ); - // initialize start position - this.rgbSliderEl.moveTo( this.rgbEl.getLeft() - 7, this.rgbEl.getTop() - 7 ); - // Create color divs and Form elements - - if (this.opacity) { - // Initialize Opacity Picker DD - this.opacitySliderEl = Ext.DomHelper.append( this.body, { 'cls': 'x-cp-slider' ,id:this.cpGetId( 'opacitySlider' ) },true); - this.opacityDD = new Ext.dd.DD( this.opacitySliderEl.id, 'opacityPicker' ); - this.opacityDD.constrainTo( this.opacityEl.id, {'top':-3,'right':-7,'bottom':-3,'left':-7} ); - this.opacityDD.onDrag = this.moveOpacityPicker.createDelegate( this ); - // initialize onclick on the rgb picker - this.opacityEl.on( 'mousedown', this.clickOpacityPicker.createDelegate( this ) ); - // initialize start position - this.opacitySliderEl.moveTo( this.opacityEl.getRight() - 7, this.opacityEl.getTop() - 3 ); - this.alpha=255; - } - - Ext.DomHelper.append( this.body, { - 'id': this.cpGetId( 'fCont' ), - 'cls': 'x-cp-formcontainer' - }, true ); - - this.formPanel = new Ext.form.FormPanel({ - renderTo:this.cpGetId( 'fCont' ), - height: this.opacity ? 202 : 182, - width:110, - frame: true, - header:false, - labelAlign: 'left', - labelWidth: 37, - forceLayout: true, - items: [{ - layout: 'column', - defaults : { - columnWidth: .5, - layout: 'form', - labelWidth: 10, - defaultType: 'numberfield', - defaults: { - width: 30, - value: 0, - minValue: 0, - maxValue: 255, - allowBlank: false, - labelSeparator: '' - } - }, - items: [{ - items: [{ - fieldLabel: 'R', - id: this.cpGetId( 'iRed' ) - },{ - fieldLabel: 'G', - id: this.cpGetId( 'iGreen' ) - },{ - fieldLabel: 'B', - id: this.cpGetId( 'iBlue' ) - }] - },{ - style:{ - paddingLeft:'3px' - }, - items: [{ - fieldLabel: 'H', - maxValue: 360, - id: this.cpGetId( 'iHue' ) - },{ - fieldLabel: 'S', - id: this.cpGetId( 'iSat' ) - },{ - fieldLabel: 'V', - id: this.cpGetId( 'iVal' ) - }] - }] - },{ - layout: 'form', - labelAlign: 'left', - items: [{ - xtype:'textfield', - width: 55, - allowBlank: false, - fieldLabel: 'HTML', - labelSeparator: '', - id: this.cpGetId( 'iHexa' ), - value: '000000' - },{ - hideLabel:true, - width:116, - layout:'form', - labelWidth:62, - hidden:!this.opacity, - items:[{ - xtype:'numberfield', - fieldLabel: 'Opacity', - id: this.cpGetId( 'iOpacity' ), - width: 30, - value: 100, - minValue: 0, - maxValue: 100, - allowBlank: false, - labelSeparator: '' - }] - },{ - hideLabel: true, - anchor: '100%', - height: 44, - width:98, - layout: 'table', - layoutConfig: { - columns: '2' - }, - id: this.cpGetId('cColorCt'), - cls:'x-cp-preview', - items: [{ - border:true, - width: 75, - height: 42, - rowspan:2, - html:[{ - id:this.cpGetId('cColor'), - cls:'x-cp-previewbox' - },{ - id:this.cpGetId('cColorOpacity'), - cls:'x-cp-previewbox' - }] - },{ - xtype: 'button', - iconCls: 'x-cp-webSafe', - id:this.cpGetId( 'cWebSafe' ), - handler: this.updateFromBox.createDelegate(this) - },{ - xtype: 'button', - iconCls: 'x-cp-inverse', - id:this.cpGetId( 'cInverse' ), - handler: this.updateFromBox.createDelegate(this) - }] - },{ - xtype:'button', - anchor: '100%', - hideLabel:true, - text: Ext.MessageBox.buttonText.ok, // For I18n - handler: this.selectColor.createDelegate(this), - style:{ - marginTop:'2px' - } - }] - }] - }); - - this.redCmp = Ext.getCmp( this.cpGetId( 'iRed' ) ); - this.greenCmp = Ext.getCmp( this.cpGetId( 'iGreen' ) ); - this.blueCmp = Ext.getCmp( this.cpGetId( 'iBlue' ) ); - this.hueCmp = Ext.getCmp( this.cpGetId( 'iHue' ) ); - this.satCmp = Ext.getCmp( this.cpGetId( 'iSat' ) ); - this.valCmp = Ext.getCmp( this.cpGetId( 'iVal' ) ); - this.hexaCmp = Ext.getCmp( this.cpGetId( 'iHexa' ) ); - - this.redCmp.on( 'change', this.updateFromIRGB.createDelegate( this ) ); - this.greenCmp.on( 'change', this.updateFromIRGB.createDelegate( this ) ); - this.blueCmp.on( 'change', this.updateFromIRGB.createDelegate( this ) ); - this.hueCmp.on( 'change', this.updateFromIHSV.createDelegate( this ) ); - this.satCmp.on( 'change', this.updateFromIHSV.createDelegate( this ) ); - this.valCmp.on( 'change', this.updateFromIHSV.createDelegate( this ) ); - this.hexaCmp.on( 'change', this.updateFromIHexa.createDelegate( this ) ); - - this.previewEl = Ext.get( this.cpGetId( 'cColor' ) ); - this.opacityPreviewEl = Ext.get( this.cpGetId( 'cColorOpacity' ) ); - this.inverseButton = Ext.getCmp(this.cpGetId( 'cInverse' )); - this.webSafeButton = Ext.getCmp(this.cpGetId( 'cWebSafe' )); - - Ext.DomHelper.append( this.body, {'tag':'br','cls':'x-cp-clearfloat'}); - }, - - onDestroy : function(){ - Ext.ux.ColorPicker.superclass.onDestroy.apply( this, arguments ); - Ext.destroyMembers(this,'formPanel','inverseButton','webSafeButton', - 'rgbEl','hueEl','opacityEl','rgbSliderEl','hueSliderEl','opacitySliderEl', - 'previewEl','opacityPreviewEl','redCmp','greenCmp','blueCmp','hueCmp','satCmp','valCmp','hexaCmp'); - }, - - /** - * - */ - cpGetId: function( postfix ) { - return this.getId() + '__' + ( postfix || 'cp' ); - }, - /** - * - */ - updateRGBPosition: function( x, y ) { - this.updateMode = 'click'; - x = x < 0 ? 0 : x; - x = x > 181 ? 181 : x; - y = y < 0 ? 0 : y; - y = y > 181 ? 181 : y; - this.HSV.s = this.getSaturation( x ); - this.HSV.v = this.getValue( y ); - this.rgbSliderEl.moveTo( this.rgbEl.getLeft() + x - 7, this.rgbEl.getTop() + y - 7, ( this.animateMove || true ) ); - this.updateColor(); - }, - /** - * - */ - updateHUEPosition: function( y ) { - this.updateMode = 'click'; - y = y < 1 ? 1 : y; - y = y > 181 ? 181 : y; - this.HSV.h = Math.round( 360 / 181 * ( 181 - y ) ); - this.hueSliderEl.moveTo( this.hueSliderEl.getLeft(), this.hueEl.getTop() + y - 7, ( this.animateMove || true ) ); - this.updateRGBPicker( this.HSV.h ); - this.updateColor(); - }, - /** - * - */ - updateOpacityPosition: function( x ) { - this.updateMode = 'click'; - x = x < 1 ? 1 : x; - x = x > 181 ? 181 : x; - this.alpha = Math.round( x/181*255); - this.opacitySliderEl.moveTo( this.opacityEl.getLeft() + x - 7, this.opacitySliderEl.getTop(), ( this.animateMove || true ) ); - this.updateColor(); - }, - /** - * - */ - clickRGBPicker: function( event, element ) { - this.updateRGBPosition( event.xy[0] - this.rgbEl.getLeft() , event.xy[1] - this.rgbEl.getTop() ); - }, - /** - * - */ - clickHUEPicker: function( event, element ) { - this.updateHUEPosition( event.xy[1] - this.hueEl.getTop() ); - }, - /** - * - */ - clickOpacityPicker: function( event, element ) { - this.updateOpacityPosition( event.xy[0] - this.opacityEl.getLeft() ); - }, - /** - * - */ - moveRGBPicker: function( event ) { - this.rgbDD.constrainTo( this.rgbEl.id, -7 ); - this.updateRGBPosition( this.rgbSliderEl.getLeft() - this.rgbEl.getLeft() + 7 , this.rgbSliderEl.getTop() - this.rgbEl.getTop() + 7 ); - }, - /** - * - */ - moveHuePicker: function( event ) { - this.hueDD.constrainTo( this.hueEl.id, {'top':-7,'right':-3,'bottom':-7,'left':-3} ); - this.updateHUEPosition( this.hueSliderEl.getTop() - this.hueEl.getTop() + 7 ); - }, - /** - * - */ - moveOpacityPicker: function( event ) { - this.opacityDD.constrainTo( this.opacityEl.id, {'top':-3,'right':-7,'bottom':-3,'left':-7} ); - this.updateOpacityPosition( this.opacitySliderEl.getLeft() - this.opacityEl.getLeft() + 7 ); - }, - /** - * - */ - updateRGBPicker: function( newValue ) { - this.updateMode = 'click'; - this.rgbEl.setStyle({ 'background-color': '#' + this.rgbToHex( this.hsvToRgb( newValue, 1, 1 ) ) }); - this.updateColor(); - }, - /** - * - */ - updateColor: function() { - var rgb = this.hsvToRgb( this.HSV.h, this.HSV.s, this.HSV.v ); - var websafe = this.websafe( rgb ); - var invert = this.invert( rgb ); - var wsInvert = this.invert( websafe ); - if( this.updateMode !== 'hexa' ) { - this.hexaCmp.setValue( this.rgbToHex( rgb ) ); - } - if( this.updateMode !== 'rgb' ) { - this.redCmp.setValue( rgb[0] ); - this.greenCmp.setValue( rgb[1] ); - this.blueCmp.setValue( rgb[2] ); - } - if( this.updateMode !== 'hsv' ) { - this.hueCmp.setValue( Math.round( this.HSV.h ) ); - this.satCmp.setValue( Math.round( this.HSV.s * 100 ) ); - this.valCmp.setValue( Math.round( this.HSV.v * 100 ) ); - } - - var htmlC = this.rgbToHex( rgb ); - var websafeC = this.rgbToHex( websafe ); - var invertC = this.rgbToHex( invert ); - - this.previewEl.setStyle({ - 'background': '#' + htmlC - }); - this.opacityPreviewEl.setStyle({ - 'background': '#' + htmlC - }); - if (this.opacity) { - this.opacityEl.setStyle({'background-color':'#'+htmlC}); - Ext.getCmp( this.cpGetId( 'iOpacity' ) ).setValue(Math.round( this.alpha/2.56 )); - this.opacityPreviewEl.applyStyles('opacity:'+(this.alpha/255)); - - } - this.webSafeButton.cpColor=websafeC; - this.inverseButton.cpColor = invertC; - this.inverseButton.setTooltip({ - title:'Inverse Color', - text:'
#'+invertC+'
' - }); - this.webSafeButton.setTooltip({ - title:'WebSafe Color', - text:'
#'+websafeC+'
' - }) - this.hueSliderEl.moveTo( this.hueSliderEl.getLeft(), this.hueEl.getTop() + this.getHPos( this.hueCmp.getValue() ) - 7, ( this.animateMove || true ) ); - this.rgbSliderEl.moveTo( this.rgbEl.getLeft() + this.getSPos( this.satCmp.getValue() / 100 ) - 7, this.hueEl.getTop() + this.getVPos( this.valCmp.getValue() / 100 ) - 7, ( this.animateMove || true ) ); - this.rgbEl.setStyle({ 'background-color': '#' + this.rgbToHex( this.hsvToRgb( this.hueCmp.getValue(), 1, 1 ) ) }); - }, - /** - * - */ - setColor: function(c) { - var s = this.splitAphaRgbHex(c.replace('#', '')); - if(!s) return; - this.alpha=s.alpha; - if (this.opacity) { - this.opacitySliderEl.moveTo( this.opacityEl.getLeft() + (s.alpha/255*181) - 7, this.opacitySliderEl.getTop(), ( this.animateMove || true ) ); - } - this.hexaCmp.setValue(s.rgbHex); - this.updateFromIHexa(); - }, - - splitAphaRgbHex : function(c) { - if (this.opacity && /^[0-9a-fA-F]{8}$/.test(c)) { - return { - alphaRgbHex : c, - alphaHex : c.substring(0, 2), - rgbHex: c.substring(2), - alpha: this.hexToDec(c.substring(0, 2)) - } - } - if (c.indexOf("rgb(") == 0) { - c = c.replace(/ /g, ""); - c = c.substring(4, c.length - 1).split(","); - c = this.rgbToHex(c[0], c[1], c[2]); - } else if (!/^[0-9a-fA-F]{6}$/.test(c)) { - c = this.colorNames[c]; - if (!Ext.isDefined(c)) { - return; - } - } - return { - alphaRgbHex : 'FF'+c, - alphaHex : 'FF', - rgbHex:c, - alpha:255 - } - }, - - /** - * - */ - updateFromIRGB: function( input, newValue, oldValue ) { - this.updateMode = 'rgb'; - var temp = this.rgbToHsv( this.redCmp.getValue(), this.greenCmp.getValue(), this.blueCmp.getValue() ); - this.HSV = { h: temp[0], s:temp[1], v:temp[2]}; - this.updateColor(); - }, - /** - * - */ - updateFromIHSV: function( input, newValue, oldValue ) { - this.updateMode = 'hsv'; - this.HSV = { h: this.hueCmp.getValue(), s:this.satCmp.getValue() / 100, v:this.valCmp.getValue() / 100}; - this.updateColor(); - }, - /** - * - */ - updateFromIHexa: function( input, newValue, oldValue ) { - this.updateMode = 'hexa'; - var temp = this.rgbToHsv( this.hexToRgb( this.hexaCmp.getValue() ) ); - this.HSV = { h: temp[0], s:temp[1], v:temp[2]}; - this.updateColor(); - }, - /** - * - */ - updateFromBox: function( button, event ) { - this.updateMode = 'click'; - var col = button.cpColor; - col = col.replace("#", ""); - var temp = this.rgbToHsv( this.hexToRgb( col )); - this.HSV = { h: temp[0], s:temp[1], v:temp[2]}; - this.updateColor(); - }, - - selectColor: function( event, element ) { - var c = this.previewEl.getColor( 'backgroundColor', '', '' ); - if (this.opacity) { - c = this.decToHex(this.alpha)+c; - } - this.fireEvent('select', this, c); - }, - /** - * Convert HSV color format to RGB color format - * @param {Integer/Array( h, s, v )} h - * @param {Integer} s (optional) - * @param {Integer} v (optional) - * @return {Array} - */ - hsvToRgb: function( h, s, v ) { - if( h instanceof Array ) { return this.hsvToRgb.call( this, h[0], h[1], h[2] ); } - var r, g, b, i, f, p, q, t; - i = Math.floor( ( h / 60 ) % 6 ); - f = ( h / 60 ) - i; - p = v * ( 1 - s ); - q = v * ( 1 - f * s ); - t = v * ( 1 - ( 1 - f ) * s ); - switch(i) { - case 0: r=v; g=t; b=p; break; - case 1: r=q; g=v; b=p; break; - case 2: r=p; g=v; b=t; break; - case 3: r=p; g=q; b=v; break; - case 4: r=t; g=p; b=v; break; - case 5: r=v; g=p; b=q; break; - } - return [this.realToDec( r ), this.realToDec( g ), this.realToDec( b )]; - }, - /** - * Convert RGB color format to HSV color format - * @param {Integer/Array( r, g, b )} r - * @param {Integer} g (optional) - * @param {Integer} b (optional) - * @return {Array} - */ - rgbToHsv: function( r, g, b ) { - if( r instanceof Array ) { return this.rgbToHsv.call( this, r[0], r[1], r[2] ); } - r = r / 255; - g = g / 255; - b = b / 255; - var min, max, delta, h, s, v; - min = Math.min( Math.min( r, g ), b ); - max = Math.max( Math.max( r, g ), b ); - delta = max - min; - switch (max) { - case min: h = 0; break; - case r: h = 60 * ( g - b ) / delta; - if ( g < b ) { h += 360; } - break; - case g: h = ( 60 * ( b - r ) / delta ) + 120; break; - case b: h = ( 60 * ( r - g ) / delta ) + 240; break; - } - s = ( max === 0 ) ? 0 : 1 - ( min / max ); - return [Math.round( h ), s, max]; - }, - /** - * Convert a float to decimal - * @param {Float} n - * @return {Integer} - */ - realToDec: function( n ) { - return Math.min( 255, Math.round( n * 256 ) ); - }, - /** - * Convert RGB color format to Hexa color format - * @param {Integer/Array( r, g, b )} r - * @param {Integer} g (optional) - * @param {Integer} b (optional) - * @return {String} - */ - rgbToHex: function( r, g, b ) { - if( r instanceof Array ) { return this.rgbToHex.call( this, r[0], r[1], r[2] ); } - return this.decToHex( r ) + this.decToHex( g ) + this.decToHex( b ); - }, - /** - * Convert an integer to hexa - * @param {Integer} n - * @return {String} - */ - decToHex: function( n ) { - var HCHARS = '0123456789ABCDEF'; - n = parseInt(n, 10); - n = ( !isNaN( n )) ? n : 0; - n = (n > 255 || n < 0) ? 0 : n; - return HCHARS.charAt( ( n - n % 16 ) / 16 ) + HCHARS.charAt( n % 16 ); - }, - /** - * Return with position of a character in this.HCHARS string - * @private - * @param {Char} c - * @return {Integer} - */ - getHCharPos: function( c ) { - var HCHARS = '0123456789ABCDEF'; - return HCHARS.indexOf( c.toUpperCase() ); - }, - /** - * Convert a hexa string to decimal - * @param {String} hex - * @return {Integer} - */ - hexToDec: function( hex ) { - var s = hex.split(''); - return ( ( this.getHCharPos( s[0] ) * 16 ) + this.getHCharPos( s[1] ) ); - }, - /** - * Convert a hexa string to RGB color format - * @param {String} hex - * @return {Array} - */ - hexToRgb: function( hex ) { - return [ this.hexToDec( hex.substr(0, 2) ), this.hexToDec( hex.substr(2, 2) ), this.hexToDec( hex.substr(4, 2) ) ]; - }, - /** - * Convert Y coordinate to HUE value - * @private - * @param {Integer} y - * @return {Integer} - */ - getHue: function( y ) { - var hue = 360 - Math.round( ( ( 181 - y ) / 181 ) * 360 ); - return hue === 360 ? 0 : hue; - }, - /** - * Convert HUE value to Y coordinate - * @private - * @param {Integer} hue - * @return {Integer} - */ - getHPos: function( hue ) { - return 181 - hue * ( 181 / 360 ); - }, - /** - * Convert X coordinate to Saturation value - * @private - * @param {Integer} x - * @return {Integer} - */ - getSaturation: function( x ) { - return x / 181; - }, - /** - * Convert Saturation value to Y coordinate - * @private - * @param {Integer} saturation - * @return {Integer} - */ - getSPos: function( saturation ) { - return saturation * 181; - }, - /** - * Convert Y coordinate to Brightness value - * @private - * @param {Integer} y - * @return {Integer} - */ - getValue: function( y ) { - return ( 181 - y ) / 181; - }, - /** - * Convert Brightness value to Y coordinate - * @private - * @param {Integer} value - * @return {Integer} - */ - getVPos: function( value ) { - return 181 - ( value * 181 ); - }, - /** - * Not documented yet - */ - checkSafeNumber: function( v ) { - if ( !isNaN( v ) ) { - v = Math.min( Math.max( 0, v ), 255 ); - var i, next; - for( i=0; i<256; i=i+51 ) { - next = i + 51; - if ( v>=i && v<=next ) { return ( v - i > 25 ) ? next : i; } - } - } - return v; - }, - /** - * Not documented yet - */ - websafe: function( r, g, b ) { - if( r instanceof Array ) { return this.websafe.call( this, r[0], r[1], r[2] ); } - return [this.checkSafeNumber( r ), this.checkSafeNumber( g ), this.checkSafeNumber( b )]; - }, - /** - * Not documented yet - */ - invert: function( r, g, b ) { - if( r instanceof Array ) { return this.invert.call( this, r[0], r[1], r[2] ); } - return [255-r,255-g,255-b]; - } -}); -/** - * - */ -Ext.ux.ColorDialog = Ext.extend( Ext.Window, { - initComponent: function() { - this.width = ( !this.width || this.width < 353 ) ? 353 : this.width; - this.applyDefaultsCP(); - Ext.ux.ColorDialog.superclass.initComponent.apply( this, arguments ); - }, - onRender: function() { - Ext.ux.ColorDialog.superclass.onRender.apply( this, arguments ); - this.renderComponent(); - } -}); -Ext.applyIf( Ext.ux.ColorDialog.prototype, Ext.ux.ColorPicker.prototype ); -/** - * - */ -Ext.ux.ColorPanel = Ext.extend( Ext.Panel, { - initComponent: function() { - this.width = ( !this.width || this.width < 333 ) ? 333 : this.width; - this.applyDefaultsCP(); - Ext.ux.ColorPanel.superclass.initComponent.apply( this, arguments ); - }, - onRender: function() { - Ext.ux.ColorPanel.superclass.onRender.apply( this, arguments ); - this.renderComponent(); - } -}); -Ext.applyIf( Ext.ux.ColorPanel.prototype, Ext.ux.ColorPicker.prototype ); -/** - * Register Color* for Lazy Rendering - */ -Ext.reg( 'colorpicker', Ext.ux.ColorPicker ); -Ext.reg( 'colordialog', Ext.ux.ColorDialog ); -Ext.reg( 'colorpanel', Ext.ux.ColorPanel ); diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/ColorPickerField.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/ColorPickerField.js deleted file mode 100644 index c9c6deb299..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/ColorPickerField.js +++ /dev/null @@ -1,183 +0,0 @@ -/** - * @class Ext.ux.form.ColorPickerField - * @extends Ext.form.TwinTriggerField - * This class makes Ext.ux.ColorPicker available as a form field. - * @license: BSD - * @author: Robert B. Williams (extjs id: vtswingkid) - * @author: Tobias Uhlig (extjs id: tobiu) - * @author: Jerome Carbou (extjs id: jcarbou) - * @constructor - * Creates a new ColorPickerField - * @param {Object} config Configuration options - * @version 1.1.3 - */ -Ext.ux.form.ColorPickerField = Ext.extend(Ext.form.TwinTriggerField, { - - editMode : 'picker', - - // private - onResize : function(w, h){ - Ext.ux.form.ColorPickerField.superclass.onResize.apply(this, arguments); - if (this.hideHtmlCode && this.colorMask) { - this.colorMask.setBox(this.createColorMaskBox()); - } - }, - - // private - onRender : function(ct, position){ - Ext.ux.form.ColorPickerField.superclass.onRender.apply(this, arguments); - - if (this.hideHtmlCode) { - this.colorMask = this.el.createProxy('x-form-colorfield-colorMask',null,true); - this.colorMask.setBox(this.createColorMaskBox()); - } - this.updateElColor(); - }, - - // private - updateElColor : function(){ - if (this.el) { - var v = this.getValue(); - var bg = 'ffffff'; - var fg = 'ffffff'; - if (v) { - alphaRgb = this.splitAphaRgbHex(v.replace('#', '')); - bg = alphaRgb.rgbHex; - fg = (this.hideHtmlCode) ? bg : this.rgbToHex(this.invert(this.hexToRgb(bg))); - } - this.el.applyStyles('background: #' + bg + ';color:#' + fg); - if (this.colorMask) { - this.colorMask.applyStyles('background: #' + bg + ';'); - } - } - }, - - // private - createColorMaskBox : function(){ - var b = this.el.getBox(); - b.x+=2; - b[0]+=2; - b.y+=2; - b[1]+=2; - b.width-=3; - b.height-=4; - b.right -= 1; - b.bottom -= 2; - return b; - }, - - initComponent : function(){ - this.editable = !this.hideHtmlCode; - this.selectOnFocus = !(this.hideHtmlCode || !this.selectOnFocus); - Ext.ux.form.ColorPickerField.superclass.initComponent.apply(this, arguments); - this.picker=-1; - switch (this.editMode){ - case 'picker' : - this.trigger1Class='x-form-colorfield-picker'; - this.triggerConfig = { - tag:'span', cls:'x-form-twin-triggers', cn:[ - {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class} - ]}; - this.picker=0; - break; - case 'palette' : - this.trigger1Class='x-form-colorfield-palette'; - this.triggerConfig = { - tag:'span', cls:'x-form-twin-triggers', cn:[ - {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class} - ]}; - this.palette=0; - break; - default : - this.trigger1Class='x-form-colorfield-palette'; - this.trigger2Class='x-form-colorfield-picker'; - this.triggerConfig = { - tag:'span', cls:'x-form-twin-triggers', cn:[ - {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class}, - {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class} - ]}; - this.palette=0; - this.picker=1; - } - - this.menus =[]; - if (this.palette>=0) { - this.menus[this.palette] = new Ext.menu.ColorMenu({ - listeners : { - select: this.onSelect - ,scope : this - } - }); - } - - if (this.picker>=0) { - this.menus[this.picker] = new Ext.ux.menu.ColorMenu({ - opacity:this.opacity, - listeners : { - select: this.onSelect - ,scope : this - } - }); - } - } - - // private - ,onSelect : function(m, c){ - this.setValue(c); - this.focus.defer(10, this); - } - - ,focus : function(selectText, delay){ - Ext.ux.form.ColorPickerField.superclass.focus.call(this, selectText && !this.hideHtmlCode, delay); - } - - ,setValue : function(v){ - if (v) { - v = v.replace('#', '').toUpperCase(); - var alphaRgb = this.splitAphaRgbHex(v); - if (this.opacity && alphaRgb) { - v=alphaRgb.alphaRgbHex; - } - v='#'+v; - } - Ext.ux.form.ColorPickerField.superclass.setValue.call(this, v); - this.updateElColor(); - } - - ,onDestroy : function(){ - Ext.destroy(this.menus,this.wrap,this.colorMask); - Ext.ux.form.ColorPickerField.superclass.onDestroy.apply(this, arguments); - } - ,onBlur : function(){ - Ext.ux.form.ColorPickerField.superclass.onBlur.apply(this, arguments); - this.setValue(this.getValue()); - } - ,onTrigger1Click : function(){ - if(this.disabled){ - return; - } - this.menus[0].show(this.el, "tl-bl?"); - if (this.picker==0) { - this.menus[0].picker.setColor(this.getValue()); - this.menus[0].picker.updateColor(); - } - } - ,onTrigger2Click : function(){ - if(this.disabled){ - return; - } - this.menus[1].show(this.el, "tl-bl?"); - if (this.picker==1) { - this.menus[1].picker.setColor(this.getValue()); - this.menus[1].picker.updateColor(); - } - } - ,hexToRgb: Ext.ux.ColorPicker.prototype.hexToRgb - ,rgbToHex: Ext.ux.ColorPicker.prototype.rgbToHex - ,decToHex: Ext.ux.ColorPicker.prototype.decToHex - ,hexToDec: Ext.ux.ColorPicker.prototype.hexToDec - ,getHCharPos: Ext.ux.ColorPicker.prototype.getHCharPos - ,invert: Ext.ux.ColorPicker.prototype.invert - ,splitAphaRgbHex: Ext.ux.ColorPicker.prototype.splitAphaRgbHex -}); -Ext.reg("colorpickerfield", Ext.ux.form.ColorPickerField); diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/colorpicker.css b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/colorpicker.css deleted file mode 100644 index 97d32cf6f1..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/colorpicker.css +++ /dev/null @@ -1,88 +0,0 @@ -.x-cp-rgbpicker, .x-cp-huepicker, .x-cp-opacitypicker, .x-cp-slider{ - position:absolute; - cursor: pointer; - _cursor: hand; - background-repeat: no-repeat; - margin: 0; - padding:0; - border: none; -} -.x-cp-slider { - left: -15px; - top: -15px; - width: 15px; - height: 15px; - background-image: url(slider-rgb.gif) !important; -} -.x-cp-rgbpicker { - left : 6px; - top : 6px; - width: 182px; - height: 182px; - background-image: url(mask.png); - background-color: red; - _background-image:none; - _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='mask.png',sizingMethod='scale'); -} -.x-cp-huepicker { - left : 196px; - top : 6px; - width: 9px; - height: 183px; - background-image: url(hue.png) !important; -} -.x-cp-opacitypicker { - left:6px; - top: 196px; - width: 183px; - height: 9px; - background-image: url(opacity.png) !important; -} -.x-cp-clearfloat { - clear: both; -} -.x-cp-formcontainer { - position:absolute; - margin: 0; - padding:0; - border: none; - top : 6px; - left : 215px; - width: 116px; -} -.x-cp-formcontainer input { - text-align: center; -} - -.x-cp-formcontainer .x-form-item{ - margin-bottom:3px; -} - -.x-cp-formcontainer .x-form-text{ - height:17px; -} - -.x-cp-preview { - background-image: url(opacity-back.gif) !important; - background-repeat: no-repeat; - background-position:0px 22px; -} -.x-cp-inverse {background-image: url(inverse.png) !important;} -.x-cp-webSafe {background-image: url(webSafe.png) !important;} -.x-cp-previewbox {width:73px;height:21px} - -.x-cp-tooltipcolorbox { - width:30; - height:16; - border:solid 1px black; -} -.x-form-field-wrap .x-form-colorfield-palette { - background-image: url('trigger-palette.gif'); -} -.x-form-field-wrap .x-form-colorfield-picker { - background-image: url('trigger-picker.gif'); -} - -.x-form-colorfield-colorMask { - position:absolute; -} diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/hue.png b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/hue.png deleted file mode 100644 index 7a3b7e3332..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/hue.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/inverse.png b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/inverse.png deleted file mode 100644 index 0ab254fdc7..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/inverse.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/mask.png b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/mask.png deleted file mode 100644 index f8d91932b3..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/mask.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/opacity-back.gif b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/opacity-back.gif deleted file mode 100644 index 0bf989909e..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/opacity-back.gif and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/opacity.png b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/opacity.png deleted file mode 100644 index 264014f9a2..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/opacity.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/slider-rgb.gif b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/slider-rgb.gif deleted file mode 100644 index 695e1927dc..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/slider-rgb.gif and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/trigger-palette.gif b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/trigger-palette.gif deleted file mode 100644 index 6e8f6eb567..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/trigger-palette.gif and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/trigger-picker.gif b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/trigger-picker.gif deleted file mode 100644 index cd8fcd661b..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/trigger-picker.gif and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/webSafe.png b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/webSafe.png deleted file mode 100644 index 772b125467..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/colorpicker/webSafe.png and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/palettecombobox/PaletteComboBox.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/palettecombobox/PaletteComboBox.js deleted file mode 100644 index 2958771ac5..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/palettecombobox/PaletteComboBox.js +++ /dev/null @@ -1,139 +0,0 @@ -Ext.namespace('Ext.ux'); - -/** - * - * Ext.ux.PaletteComboBox Class - * - * Example usage : - * - *
- *

-var palettes = [
-    [0, ["#FF0000", "#FF4400", "#FF8800", "#FFCC00"]],
-    [1, ["#00FF00", "#00FF44", "#00FF88", "#00FFCC"]]
-];
-
-var combo = new Ext.ux.PaletteComboBox({
-    fieldLabel:'IconCombo',
-    store: new Ext.data.SimpleStore({
-            fields: ['value', 'colors'],
-            data: palettes
-    }),
-    valueField: 'value',
-    triggerAction: 'all',
-    mode: 'local'
-});
-
- -

-
-
- */ - -Ext.ux.PaletteComboBox = Ext.extend(Ext.form.ComboBox, { - - cls: 'x-combo-palette', - - initComponent:function() { - - var paletteTpl = '
' - + '' - + '
 
' - + '
' - + '
'; - - Ext.apply(this, { - paletteTpl: paletteTpl, - - tpl: '' - + '
' - + paletteTpl - + '
' - }); - - // call parent initComponent - Ext.ux.PaletteComboBox.superclass.initComponent.call(this); - - }, - - onRender:function(ct, position) { - // call parent onRender - Ext.ux.PaletteComboBox.superclass.onRender.call(this, ct, position); - - // adjust styles - this.wrap.applyStyles({position:'relative'}); - - this.el.addClass('ux-icon-combo-input'); - - // add div for icon - var el = Ext.DomHelper.append(this.el.up('div.x-form-field-wrap'), { - tag: 'div', cls: 'x-combo-palette-input-value', - style: 'width:' + this.el.getWidth(true) + 'px' - }); - this.icon = Ext.DomHelper.append(el, { - tag: 'div', style:'width:500px' - }); - }, - - setIconCls:function() { - var rec = this.store.query(this.valueField, this.getValue()).itemAt(0); - if(rec) { - var t = new Ext.XTemplate(this.paletteTpl); - var palette = {colors: rec.get('colors')}; - t.overwrite(this.icon, palette); - } - }, - - setValue: function(value) { - Ext.ux.PaletteComboBox.superclass.setValue.call(this, value); - - this.setIconCls(); - - // don't show the color value in the input - this.el.dom.value = ""; - }, - - onResize: function(w, h) { - Ext.ux.PaletteComboBox.superclass.onResize.apply(this, arguments); - - // resize the icon div's parent based on the input field's - // new size - Ext.fly(this.icon).up("div.x-combo-palette-input-value").setWidth( - this.el.getWidth(true) - ); - } -}); - -Ext.reg('ext.ux.palettecombo', Ext.ux.PaletteComboBox); - diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/palettecombobox/palettecombobox.ux.css b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/palettecombobox/palettecombobox.ux.css deleted file mode 100644 index a806cc3bdb..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/palettecombobox/palettecombobox.ux.css +++ /dev/null @@ -1,26 +0,0 @@ -.x-combo-palette-item { - padding: 2px !important; -} -.x-combo-palette-input-value { - position: absolute; - top: 0; - left: 0; - overflow: hidden; - height:18px; - padding: 3px; -} -.x-combo-palette { - position: relative; -} -.x-combo-palette div { - float: left; - -moz-outline: 0 none; - outline: 0 none; - padding: 1px; -} -.x-combo-palette div span { - display: block; - height: 14px; - line-height: 14px; - width: 14px; -} diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/NumberSpinner.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/NumberSpinner.js deleted file mode 100644 index 007728fb8d..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/NumberSpinner.js +++ /dev/null @@ -1,33 +0,0 @@ -Ext.namespace('Ext.ux'); - -Ext.ux.NumberSpinner = Ext.extend(Ext.form.NumberField, { - defaultValue: null, - - initComponent: function() { - this.addSpinner(); - Ext.ux.NumberSpinner.superclass.initComponent.apply(this, arguments); - }, - - addSpinner: function() { - var spinner=new Ext.ux.form.Spinner({ - strategy: new Ext.ux.form.Spinner.NumberStrategy({ - minValue: this.minValue, - maxValue: this.maxValue, - allowDecimals: this.allowDecimals, - decimalPrecision: this.decimalPrecision, - defaultValue: this.defaultValue||0, - incrementValue: this.incrementValue||1 - }) - }); - this.on('render', function() { - spinner.applyToMarkup(this.getEl()); - spinner.splitter.hide(); //not well placed and not very useful => I get rid of it - }, this); - spinner.on('spin', function() { - this.fireEvent('change', this, null, null); - }, this); - this.on('enable', spinner.enable, spinner); - this.on('disable', spinner.disable, spinner); - } -}); -Ext.reg('numberspinner', Ext.ux.NumberSpinner); \ No newline at end of file diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/Spinner.css b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/Spinner.css deleted file mode 100644 index 01362a7f70..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/Spinner.css +++ /dev/null @@ -1,50 +0,0 @@ -.x-form-spinner-proxy{ - /*background-color:#ff00cc;*/ -} -.x-form-field-wrap .x-form-spinner-trigger { - background:transparent url(spinner.gif) no-repeat 0 0; -} - -.x-form-field-wrap .x-form-spinner-overup{ - background-position:-17px 0; -} -.x-form-field-wrap .x-form-spinner-clickup{ - background-position:-34px 0; -} -.x-form-field-wrap .x-form-spinner-overdown{ - background-position:-51px 0; -} -.x-form-field-wrap .x-form-spinner-clickdown{ - background-position:-68px 0; -} - - -.x-trigger-wrap-focus .x-form-spinner-trigger{ - background-position:-85px 0; -} -.x-trigger-wrap-focus .x-form-spinner-overup{ - background-position:-102px 0; -} -.x-trigger-wrap-focus .x-form-spinner-clickup{ - background-position:-119px 0; -} -.x-trigger-wrap-focus .x-form-spinner-overdown{ - background-position:-136px 0; -} -.x-trigger-wrap-focus .x-form-spinner-clickdown{ - background-position:-153px 0; -} -.x-trigger-wrap-focus .x-form-trigger{ - border-bottom: 1px solid #7eadd9; -} - -.x-form-field-wrap .x-form-spinner-splitter { - line-height:1px; - font-size:1px; - background:transparent url(spinner-split.gif) no-repeat 0 0; - position:absolute; - cursor: n-resize; -} -.x-trigger-wrap-focus .x-form-spinner-splitter{ - background-position:-14px 0; -} diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/Spinner.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/Spinner.js deleted file mode 100644 index 43383203a5..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/Spinner.js +++ /dev/null @@ -1,461 +0,0 @@ -/** - * Copyright (c) 2008, Steven Chim - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Ext.ux.form.Spinner Class - * - * @author Steven Chim - * @version Spinner.js 2008-08-27 v0.35 - * - * @class Ext.ux.form.Spinner - * @extends Ext.form.TriggerField - */ - -Ext.namespace("Ext.ux.form"); - -Ext.ux.form.Spinner = function(config){ - Ext.ux.form.Spinner.superclass.constructor.call(this, config); - this.addEvents({ - 'spin' : true, - 'spinup' : true, - 'spindown' : true - }); -}; - -Ext.ux.form.Spinner.Strategy = function(config){ - Ext.apply(this, config); -}; - -Ext.extend(Ext.ux.form.Spinner.Strategy, Ext.util.Observable, { - defaultValue : 0, - minValue : undefined, - maxValue : undefined, - incrementValue : 1, - alternateIncrementValue : 5, - validationTask : new Ext.util.DelayedTask(), - - onSpinUp : function(field){ - this.spin(field, false, false); - }, - - onSpinDown : function(field){ - this.spin(field, true, false); - }, - - onSpinUpAlternate : function(field){ - this.spin(field, false, true); - }, - - onSpinDownAlternate : function(field){ - this.spin(field, true, true); - }, - - spin : function(field, down, alternate){ - this.validationTask.delay(500, function(){field.validate();}); - //extend - }, - - fixBoundries : function(value){ - return value; - //overwrite - } - -}); - -/*** - * Concrete Strategy: Numbers - */ -Ext.ux.form.Spinner.NumberStrategy = function(config){ - Ext.ux.form.Spinner.NumberStrategy.superclass.constructor.call(this, config); -}; - -Ext.extend(Ext.ux.form.Spinner.NumberStrategy, Ext.ux.form.Spinner.Strategy, { - - allowDecimals : true, - decimalPrecision : 2, - - spin : function(field, down, alternate){ - Ext.ux.form.Spinner.NumberStrategy.superclass.spin.call(this, field, down, alternate); - - var v = parseFloat(field.getValue()); - var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue; - - (down == true) ? v -= incr : v += incr ; - v = (isNaN(v)) ? this.defaultValue : v; - v = this.fixBoundries(v); - field.setRawValue(v); - }, - - fixBoundries : function(value){ - var v = value; - - if(this.minValue != undefined && v < this.minValue){ - v = this.minValue; - } - if(this.maxValue != undefined && v > this.maxValue){ - v = this.maxValue; - } - - return this.fixPrecision(v); - }, - - // private - fixPrecision : function(value){ - var nan = isNaN(value); - if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){ - return nan ? '' : value; - } - return parseFloat(parseFloat(value).toFixed(this.decimalPrecision)); - } -}); - - -/*** - * Concrete Strategy: Date - */ -Ext.ux.form.Spinner.DateStrategy = function(config){ - Ext.ux.form.Spinner.DateStrategy.superclass.constructor.call(this, config); -}; - -Ext.extend(Ext.ux.form.Spinner.DateStrategy, Ext.ux.form.Spinner.Strategy, { - defaultValue : new Date(), - format : "Y-m-d", - incrementValue : 1, - incrementConstant : Date.DAY, - alternateIncrementValue : 1, - alternateIncrementConstant : Date.MONTH, - - spin : function(field, down, alternate){ - Ext.ux.form.Spinner.DateStrategy.superclass.spin.call(this); - - var v = field.getRawValue(); - - v = Date.parseDate(v, this.format); - var dir = (down == true) ? -1 : 1 ; - var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue; - var dtconst = (alternate == true) ? this.alternateIncrementConstant : this.incrementConstant; - - if(typeof this.defaultValue == 'string'){ - this.defaultValue = Date.parseDate(this.defaultValue, this.format); - } - - v = (v) ? v.add(dtconst, dir*incr) : this.defaultValue; - - v = this.fixBoundries(v); - field.setRawValue(Ext.util.Format.date(v,this.format)); - }, - - //private - fixBoundries : function(date){ - var dt = date; - var min = (typeof this.minValue == 'string') ? Date.parseDate(this.minValue, this.format) : this.minValue ; - var max = (typeof this.maxValue == 'string') ? Date.parseDate(this.maxValue, this.format) : this.maxValue ; - - if(this.minValue != undefined && dt < min){ - dt = min; - } - if(this.maxValue != undefined && dt > max){ - dt = max; - } - - return dt; - } - -}); - - -/*** - * Concrete Strategy: Time - */ -Ext.ux.form.Spinner.TimeStrategy = function(config){ - Ext.ux.form.Spinner.TimeStrategy.superclass.constructor.call(this, config); -}; - -Ext.extend(Ext.ux.form.Spinner.TimeStrategy, Ext.ux.form.Spinner.DateStrategy, { - format : "H:i", - incrementValue : 1, - incrementConstant : Date.MINUTE, - alternateIncrementValue : 1, - alternateIncrementConstant : Date.HOUR -}); - - - -Ext.extend(Ext.ux.form.Spinner, Ext.form.TriggerField, { - triggerClass : 'x-form-spinner-trigger', - splitterClass : 'x-form-spinner-splitter', - - alternateKey : Ext.EventObject.shiftKey, - strategy : undefined, - - //private - onRender : function(ct, position){ - Ext.ux.form.Spinner.superclass.onRender.call(this, ct, position); - - this.splitter = this.wrap.createChild({tag:'div', cls:this.splitterClass, style:'width:13px; height:2px;'}); - this.splitter.show().setRight( (Ext.isIE) ? 1 : 2 ); - this.splitter.show().setTop(10); - - this.proxy = this.trigger.createProxy('', this.splitter, true); - this.proxy.addClass("x-form-spinner-proxy"); - this.proxy.setStyle('left','0px'); - this.proxy.setSize(14, 1); - this.proxy.hide(); - this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", {dragElId: this.proxy.id}); - - this.initSpinner(); - }, - - //private - initTrigger : function(){ - this.trigger.addClassOnOver('x-form-trigger-over'); - this.trigger.addClassOnClick('x-form-trigger-click'); - }, - - //private - initSpinner : function(){ - this.keyNav = new Ext.KeyNav(this.el, { - "up" : function(e){ - e.preventDefault(); - this.onSpinUp(); - }, - - "down" : function(e){ - e.preventDefault(); - this.onSpinDown(); - }, - - "pageUp" : function(e){ - e.preventDefault(); - this.onSpinUpAlternate(); - }, - - "pageDown" : function(e){ - e.preventDefault(); - this.onSpinDownAlternate(); - }, - - scope : this - }); - - this.repeater = new Ext.util.ClickRepeater(this.trigger); - this.repeater.on("click", this.onTriggerClick, this, {preventDefault:true}); - this.trigger.on("mouseover", this.onMouseOver, this, {preventDefault:true}); - this.trigger.on("mouseout", this.onMouseOut, this, {preventDefault:true}); - this.trigger.on("mousemove", this.onMouseMove, this, {preventDefault:true}); - this.trigger.on("mousedown", this.onMouseDown, this, {preventDefault:true}); - this.trigger.on("mouseup", this.onMouseUp, this, {preventDefault:true}); - this.wrap.on("mousewheel", this.handleMouseWheel, this); - - this.dd.setXConstraint(0, 0, 10); - this.dd.setYConstraint(1500, 1500, 10); - this.dd.endDrag = this.endDrag.createDelegate(this); - this.dd.startDrag = this.startDrag.createDelegate(this); - this.dd.onDrag = this.onDrag.createDelegate(this); - - /* - jsakalos suggestion - http://extjs.com/forum/showthread.php?p=121850#post121850 */ - if('object' == typeof this.strategy && this.strategy.xtype) { - switch(this.strategy.xtype) { - case 'number': - this.strategy = new Ext.ux.form.Spinner.NumberStrategy(this.strategy); - break; - - case 'date': - this.strategy = new Ext.ux.form.Spinner.DateStrategy(this.strategy); - break; - - case 'time': - this.strategy = new Ext.ux.form.Spinner.TimeStrategy(this.strategy); - break; - - default: - delete(this.strategy); - break; - } - delete(this.strategy.xtype); - } - - if(this.strategy == undefined){ - this.strategy = new Ext.ux.form.Spinner.NumberStrategy(); - } - }, - - //private - onMouseOver : function(){ - if(this.disabled){ - return; - } - var middle = this.getMiddle(); - this.__tmphcls = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown'; - this.trigger.addClass(this.__tmphcls); - }, - - //private - onMouseOut : function(){ - this.trigger.removeClass(this.__tmphcls); - }, - - //private - onMouseMove : function(){ - if(this.disabled){ - return; - } - var middle = this.getMiddle(); - if( ((Ext.EventObject.getPageY() > middle) && this.__tmphcls == "x-form-spinner-overup") || - ((Ext.EventObject.getPageY() < middle) && this.__tmphcls == "x-form-spinner-overdown")){ - } - }, - - //private - onMouseDown : function(){ - if(this.disabled){ - return; - } - var middle = this.getMiddle(); - this.__tmpccls = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown'; - this.trigger.addClass(this.__tmpccls); - }, - - //private - onMouseUp : function(){ - this.trigger.removeClass(this.__tmpccls); - }, - - //private - onTriggerClick : function(){ - if(this.disabled || this.getEl().dom.readOnly){ - return; - } - var middle = this.getMiddle(); - var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down'; - this['onSpin'+ud](); - }, - - //private - getMiddle : function(){ - var t = this.trigger.getTop(); - var h = this.trigger.getHeight(); - var middle = t + (h/2); - return middle; - }, - - //private - //checks if control is allowed to spin - isSpinnable : function(){ - if(this.disabled || this.getEl().dom.readOnly){ - Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly - return false; - } - return true; - }, - - handleMouseWheel : function(e){ - //disable scrolling when not focused - if(this.wrap.hasClass('x-trigger-wrap-focus') == false){ - return; - } - - var delta = e.getWheelDelta(); - if(delta > 0){ - this.onSpinUp(); - e.stopEvent(); - } else if(delta < 0){ - this.onSpinDown(); - e.stopEvent(); - } - }, - - //private - startDrag : function(){ - this.proxy.show(); - this._previousY = Ext.fly(this.dd.getDragEl()).getTop(); - }, - - //private - endDrag : function(){ - this.proxy.hide(); - }, - - //private - onDrag : function(){ - if(this.disabled){ - return; - } - var y = Ext.fly(this.dd.getDragEl()).getTop(); - var ud = ''; - - if(this._previousY > y){ud = 'Up';} //up - if(this._previousY < y){ud = 'Down';} //down - - if(ud != ''){ - this['onSpin'+ud](); - } - - this._previousY = y; - }, - - //private - onSpinUp : function(){ - if(this.isSpinnable() == false) { - return; - } - if(Ext.EventObject.shiftKey == true){ - this.onSpinUpAlternate(); - return; - }else{ - this.strategy.onSpinUp(this); - } - this.fireEvent("spin", this); - this.fireEvent("spinup", this); - }, - - //private - onSpinDown : function(){ - if(this.isSpinnable() == false) { - return; - } - if(Ext.EventObject.shiftKey == true){ - this.onSpinDownAlternate(); - return; - }else{ - this.strategy.onSpinDown(this); - } - this.fireEvent("spin", this); - this.fireEvent("spindown", this); - }, - - //private - onSpinUpAlternate : function(){ - if(this.isSpinnable() == false) { - return; - } - this.strategy.onSpinUpAlternate(this); - this.fireEvent("spin", this); - this.fireEvent("spinup", this); - }, - - //private - onSpinDownAlternate : function(){ - if(this.isSpinnable() == false) { - return; - } - this.strategy.onSpinDownAlternate(this); - this.fireEvent("spin", this); - this.fireEvent("spindown", this); - } - -}); - -Ext.reg('uxspinner', Ext.ux.form.Spinner); diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/SpinnerStrategy.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/SpinnerStrategy.js deleted file mode 100644 index b7bd8fbf83..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/SpinnerStrategy.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2008, Steven Chim - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/*** - * Abstract Strategy - */ - -/* - * content transfered to Spinner.js file because of build errors. - */ \ No newline at end of file diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/spinner-split.gif b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/spinner-split.gif deleted file mode 100644 index 72811461bb..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/spinner-split.gif and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/spinner.gif b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/spinner.gif deleted file mode 100644 index 4e72f53d09..0000000000 Binary files a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/spinner/spinner.gif and /dev/null differ diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/tree/TreeRecordNode.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/tree/TreeRecordNode.js deleted file mode 100644 index c58acffa9a..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/tree/TreeRecordNode.js +++ /dev/null @@ -1,147 +0,0 @@ -Ext.namespace("Ext.ux.tree"); - -/** api: (define) - * module = Ext.ux.tree - * class = TreeRecordNode - * base_link = `Ext.tree.TreeNode `_ - */ - -/** api: constructor - * .. class:: TreeRecordNode(config) - * - * A subclass of ``Ext.tree.TreeNode`` that is connected to an - * ``Ext.data.Record`` by setting the node's record property. - * - * The node's text property defaults to the record 'text' attribute. - * - * To use this node type in a ``TreePanel`` config, set ``nodeType`` to - * "ux_treerecordnode". - */ -Ext.ux.tree.TreeRecordNode = Ext.extend(Ext.tree.TreeNode, { - - /** api: config[store] - * :class:`Ext.data.Store` - * The store containing the record that this node represents. - */ - store: null, - - /** api: config[record] - * :class:``Ext.data.Record`` - * The record this node is bound to. - */ - record: null, - - /** api: config[childNodeType] - * ``Ext.tree.Node or String`` - * Node class or nodeType of childnodes for this node. A node type provided - * here needs to have an add method, with a scope argument. This method - * will be run by this node in the context of this node, to create child nodes. - */ - childNodeType: null, - - /** private: method[constructor] - * Private constructor override. - */ - constructor: function(config) { - config.leaf = config.leaf || !config.children; - config.qtip = config.record.get('qtip') || undefined; - Ext.apply(this, { - record: config.record, - store: config.store, - childNodeType: config.childNodeType - }); - Ext.ux.tree.TreeRecordNode.superclass.constructor.apply(this, arguments); - }, - - /** private: method[render] - * :param bulkRender: ``Boolean`` - */ - render: function(bulkRender) { - if (!this.rendered) { - var ui = this.getUI(); - - if(this.record) { - if(!this.text) { - this.text = this.record.get('text'); // which member to display? - } - if(this.childNodeType) { - this.addChildNodes(); - } - ui.show(); - } else { - ui.hide(); - } - - if(this.store instanceof Ext.data.Store) { - this.addStoreEventHandlers(); - } - } - Ext.ux.tree.TreeRecordNode.superclass.render.call(this, bulkRender); - }, - - /** private: method[addStoreEventHandlers] - * Adds handlers that make sure the node disappeares when the record is - * removed from the store, and appears when it is re-added. - */ - addStoreEventHandlers: function() { - this.store.on({ - "add": this.onStoreAdd, - "remove": this.onStoreRemove, - scope: this - }); - }, - - /** private: method[onStoreAdd] - * :param store: ``Ext.data.Store`` - * :param records: ``Array(Ext.data.Record)`` - * :param index: ``Nmber`` - * handler for add events on the store - */ - onStoreAdd: function(store, records, index) { - for(var i=0; i`_ - */ - -/** api: constructor - * .. class:: TreeStoreNode - * - * A subclass of ``Ext.tree.TreeNode`` that will collect all records - * from a store. Only records that have displayInTree set to true - * will be included. - * - * To use this node type in ``TreePanel`` config, set nodeType to - * "ux_treestorenode". - */ -Ext.ux.tree.TreeStoreNode = Ext.extend(Ext.tree.AsyncTreeNode, { - - /** api: config[store] - * :class:`Ext.data.Store` - * The store containing record nodes to be displayed in the container. - */ - store: null, - - /** api: config[defaults] - * ``Object`` - * A configuration object passed to all nodes that this container creates. - */ - defaults: null, - - /** private: method[constructor] - * Private constructor override. - */ - constructor: function(config) { - this.store = config.store; - this.defaults = config.defaults || {}; - Ext.ux.tree.TreeStoreNode.superclass.constructor.apply(this, arguments); - }, - - /** private: method[render] - * :param bulkRender: ``Boolean`` - */ - render: function(bulkRender) { - if (!this.rendered) { - this.store.each(function(record) { - this.addRecordNode(record); - }, this); - this.store.on({ - "load": this.onStoreLoad, - "add": this.onStoreAdd, - "remove": this.onStoreRemove, - scope: this - }); - } - Ext.ux.tree.TreeStoreNode.superclass.render.call(this, bulkRender); - }, - - /** private: method[onStoreLoad] - * :param store: ``Ext.data.Store`` - * :param records: ``Array(Ext.data.Record)`` - * :param options: ``Object`` - * - * Listener for the store's load event. - */ - onStoreLoad: function(store, records, options) { - // if options.add: nodes have already been added to tree on "add" event - if (options && !options.add) { - // options.add is false: let's remove every child from the tree - this.eachChild(function(node) { node.remove(); }); - if (!Ext.isArray(records)) { - records = [records]; - } - for (var i = 0; i < records.length; i++) { - this.addRecordNode(records[i]); - } - } - }, - - /** private: method[onStoreAdd] - * :param store: ``Ext.data.Store`` - * :param records: ``Array(Ext.data.Record)`` - * :param index: ``Number`` - * - * Listener for the store's add event. - */ - onStoreAdd: function(store, records, index) { - if(!this._reordering) { - var nodeIndex = this.recordIndexToNodeIndex(index+records.length-1); - for(var i=0; i=0; --i) { - if(store.getAt(i).get('displayInTree') === true) { - ++nodeIndex; - if(index === i || nodeIndex > nodeCount-1) { - break; - } - } - }; - return nodeIndex; - }, - - /** private: method[nodeIndexToRecordIndex] - * :param index: ``Number`` The child node index. - * :return: ``Number`` The appropriate record index for the node. - * - * Convert a child node index to a record index. - */ - nodeIndexToRecordIndex: function(index) { - var store = this.store; - var count = store.getCount(); - var nodeIndex = -1; - for(var i=count-1; i>=0; --i) { - if(store.getAt(i).get('displayInTree') === true) { - ++nodeIndex; - if(index === nodeIndex) { - break; - } - } - } - return i; - }, - - /** private: method[addRecordNode] - * :param record: ``Ext.data.Record`` The record to be added. - * :param index: ``Number`` Optional index for the new record. Default is 0. - * - * Adds a child node representing a record - */ - addRecordNode: function(record, index) { - index = index || 0; - if (record.get('displayInTree') === true) { - var node = new Ext.ux.tree.TreeRecordNode(Ext.applyIf({ - record: record, - store: this.store - }, this.defaults)); - var sibling = this.item(index); - if(sibling) { - this.insertBefore(node, sibling); - } else { - this.appendChild(node); - } - node.on("move", this.onChildMove, this); - } - }, - - /** private: method[removeRecordNode] - * :param record: ``Ext.data.Record`` The record to be removed. - * - * Removes a child node representing a record - */ - removeRecordNode: function(record) { - if (record.get('displayInTree') === true) { - var node = this.findChildBy(function(node) { - return node.record === record; - }); - if(node) { - node.un("move", this.onChildMove, this); - node.remove(); - } - } - }, - - /** private: method[onChildMove] - * :param tree: ``Ext.data.Tree`` - * :param node: ``Ext.tree.TreeNode`` - * :param oldParent: ``Ext.tree.TreeNode`` - * :param newParent: ``Ext.tree.TreeNode`` - * :param index: ``Number`` - * - * Listener for child node "move" events. This updates the order of - * records in the store based on new node order if the node has not - * changed parents. - */ - onChildMove: function(tree, node, oldParent, newParent, index) { - if(oldParent === newParent) { - var newRecordIndex = this.nodeIndexToRecordIndex(index); - var oldRecordIndex = this.store.findBy(function(record) { - return record === node.record; - }); - // remove the record and re-insert it at the correct index - var record = this.store.getAt(oldRecordIndex); - this._reordering = true; - this.store.remove(record); - this.store.insert(newRecordIndex, [record]); - delete this._reordering; - } - } - -}); - -/** - * NodeType: ux_treestorenode - */ -Ext.tree.TreePanel.nodeTypes.ux_treestorenode = Ext.ux.tree.TreeStoreNode; diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/tree/XmlTreeLoader.js b/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/tree/XmlTreeLoader.js deleted file mode 100644 index b345a21a93..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/lib/Ext.ux/widgets/tree/XmlTreeLoader.js +++ /dev/null @@ -1,51 +0,0 @@ -Ext.namespace('Ext.ux.tree'); - -Ext.ux.tree.XmlTreeLoader = Ext.extend(Ext.tree.TreeLoader, { - - requestData : function(node, callback){ - if(this.fireEvent("beforeload", this, node, callback) !== false){ - this.transId = Ext.Ajax.request({ - method:this.requestMethod, - url: this.dataUrl||this.url, - success: this.handleResponse, - failure: this.handleFailure, - scope: this, - argument: {callback: callback, node: node}, - xmlData: this.parseInput(this, node) - }); - }else{ - // if the load is cancelled, make sure we notify - // the node that we are done - if(typeof callback == "function"){ - callback(); - } - } - }, - - processResponse : function(response, node, callback){ - var xml = response.responseText; - try { - var o = this.parseOutput(this, xml); - node.beginUpdate(); - if(node.store && node.store.loadData) { - node.store.loadData(o); - } - else { - for(var i = 0, len = o.length; i < len; i++){ - var n = this.createNode(o[i]); - if(n){ - node.appendChild(n); - } - } - } - node.endUpdate(); - if(typeof callback == "function"){ - callback(this, node); - } - }catch(e){ - this.handleFailure(response); - } - } - -}); - diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/tests/index.html b/catalogapp/src/main/webapp/lib/Ext.ux/tests/index.html deleted file mode 100644 index d4a8dad7e6..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/tests/index.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/tests/lib/Ext.ux/widgets/tree/TreeRecordNode.html b/catalogapp/src/main/webapp/lib/Ext.ux/tests/lib/Ext.ux/widgets/tree/TreeRecordNode.html deleted file mode 100644 index 17c940c28b..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/tests/lib/Ext.ux/widgets/tree/TreeRecordNode.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - -
- - diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/tests/lib/Ext.ux/widgets/tree/TreeStoreNode.html b/catalogapp/src/main/webapp/lib/Ext.ux/tests/lib/Ext.ux/widgets/tree/TreeStoreNode.html deleted file mode 100644 index 96580d26c5..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/tests/lib/Ext.ux/widgets/tree/TreeStoreNode.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - -
- - diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/tests/list-tests.html b/catalogapp/src/main/webapp/lib/Ext.ux/tests/list-tests.html deleted file mode 100644 index 38dd821109..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/tests/list-tests.html +++ /dev/null @@ -1,4 +0,0 @@ -
    -
  • lib/Ext.ux/widgets/tree/TreeRecordNode.html
  • -
  • lib/Ext.ux/widgets/tree/TreeStoreNode.html
  • -
diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/tests/run-tests.html b/catalogapp/src/main/webapp/lib/Ext.ux/tests/run-tests.html deleted file mode 100644 index 46bea5d640..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/tests/run-tests.html +++ /dev/null @@ -1,2408 +0,0 @@ - - Run the testsuite - - - - - - - -
-
Test pages:
-
- -
-
-
- - - - -
- do not close windows opened by tests -
-
- -

Record mouse input for the page:

-

-

or enter page url:

-

-
-
- -
-
-Results: - - -
-
-
-
-
-
- - - - - - --
- -

-  - -

- -
 
-

-cursor is over -

- -

- keyboard control: press - ctrl - - shift - -

- -

-s -to start recording - -

- -

-h -to hide/show this window -

- -

-m -to record mousemove - -

- -

-p -to pause recording - -

- -

-c -to add checkpoint -

- -

-checkpoints: -

-
-
-
- -
- diff --git a/catalogapp/src/main/webapp/lib/Ext.ux/tests/xml_eq.js b/catalogapp/src/main/webapp/lib/Ext.ux/tests/xml_eq.js deleted file mode 100644 index f3e92feec2..0000000000 --- a/catalogapp/src/main/webapp/lib/Ext.ux/tests/xml_eq.js +++ /dev/null @@ -1,311 +0,0 @@ -/** - * File: xml_eq.js - * Adds a xml_eq method to AnotherWay test objects. - * - */ - -(function() { - - /** - * Function: createNode - * Given a string, try to create an XML DOM node. Throws string messages - * on failure. - * - * Parameters: - * text - {String} An XML string. - * - * Returns: - * {DOMElement} An element node. - */ - function createNode(text) { - - var index = text.indexOf('<'); - if(index > 0) { - text = text.substring(index); - } - - var doc; - if(window.ActiveXObject && !this.xmldom) { - doc = new ActiveXObject("Microsoft.XMLDOM"); - try { - doc.loadXML(text); - } catch(err) { - throw "ActiveXObject loadXML failed: " + err; - } - } else if(window.DOMParser) { - try { - doc = new DOMParser().parseFromString(text, 'text/xml'); - } catch(err) { - throw "DOMParser.parseFromString failed"; - } - if(doc.documentElement && doc.documentElement.nodeName == "parsererror") { - throw "DOMParser.parseFromString returned parsererror"; - } - } else { - var req = new XMLHttpRequest(); - req.open("GET", "data:text/xml;charset=utf-8," + - encodeURIComponent(text), false); - if(req.overrideMimeType) { - req.overrideMimeType("text/xml"); - } - req.send(null); - doc = req.responseXML; - } - - var root = doc.documentElement; - if(!root) { - throw "no documentElement"; - } - return root; - } - - /** - * Function assertEqual - * Test two objects for equivalence (based on ==). Throw an exception - * if not equivalent. - * - * Parameters: - * got - {Object} - * expected - {Object} - * msg - {String} The message to be thrown. This message will be appended - * with ": got {got} but expected {expected}" where got and expected are - * replaced with string representations of the above arguments. - */ - function assertEqual(got, expected, msg) { - if(got === undefined) { - got = "undefined"; - } else if (got === null) { - got = "null"; - } - if(expected === undefined) { - expected = "undefined"; - } else if (expected === null) { - expected = "null"; - } - if(got != expected) { - throw msg + ": got '" + got + "' but expected '" + expected + "'"; - } - } - - /** - * Function assertElementNodesEqual - * Test two element nodes for equivalence. Nodes are considered equivalent - * if they are of the same type, have the same name, have the same - * namespace prefix and uri, and if all child nodes are equivalent. - * Throws a message as exception if not equivalent. - * - * Parameters: - * got - {DOMElement} - * expected - {DOMElement} - * options - {Object} Optional object for configuring test options. - * - * Valid options: - * prefix - {Boolean} Compare element and attribute - * prefixes (namespace uri always tested). Default is false. - * includeWhiteSpace - {Boolean} Include whitespace only nodes when - * comparing child nodes. Default is false. - */ - function assertElementNodesEqual(got, expected, options) { - var testPrefix = (options && options.prefix === true); - - // compare types - assertEqual(got.nodeType, expected.nodeType, "Node type mismatch"); - - // compare names - var gotName = testPrefix ? - got.nodeName : got.nodeName.split(":").pop(); - var expName = testPrefix ? - expected.nodeName : expected.nodeName.split(":").pop(); - assertEqual(gotName, expName, "Node name mismatch"); - - // for text nodes compare value - if(got.nodeType == 3) { - assertEqual( - got.nodeValue, expected.nodeValue, "Node value mismatch" - ); - } - // for element type nodes compare namespace, attributes, and children - else if(got.nodeType == 1) { - - // test namespace alias and uri - if(got.prefix || expected.prefix) { - if(testPrefix) { - assertEqual( - got.prefix, expected.prefix, - "Bad prefix for " + got.nodeName - ); - } - } - if(got.namespaceURI || expected.namespaceURI) { - assertEqual( - got.namespaceURI, expected.namespaceURI, - "Bad namespaceURI for " + got.nodeName - ); - } - - // compare attributes - disregard xmlns given namespace handling above - var gotAttrLen = 0; - var gotAttr = {}; - var expAttrLen = 0; - var expAttr = {}; - var ga, ea, gn, en; - for(var i=0; i org.georchestra root - 15.12-SNAPSHOT + 21.0-SNAPSHOT georchestra-commons jar @@ -13,27 +13,52 @@ Common classes shared across geOrchestra apps - javax.servlet - servlet-api - 2.4 - provided + org.projectlombok + lombok + + + com.google.guava + guava - org.springframework - spring-webmvc - ${spring.version} - compile + junit + junit + test + + + javax.servlet + javax.servlet-api + provided log4j log4j - 1.2.17 provided commons-io commons-io - 1.4 + + + + net.revelc.code.formatter + formatter-maven-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + verify + + jar-no-fork + + + + + + diff --git a/commons/src/main/java/org/georchestra/commons/WaitForDb.java b/commons/src/main/java/org/georchestra/commons/WaitForDb.java new file mode 100644 index 0000000000..1cc231f68a --- /dev/null +++ b/commons/src/main/java/org/georchestra/commons/WaitForDb.java @@ -0,0 +1,75 @@ +package org.georchestra.commons; + +import javax.annotation.PostConstruct; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class WaitForDb { + + private String url; + private String username; + private String password; + private String driverClassName; + + @PostConstruct + public void test() { + try { + Class.forName(driverClassName); + } catch (ClassNotFoundException e) { + System.err.printf("CONFIGURATION ERROR: Unable to load JDBC driver '%s'", driverClassName); + throw new RuntimeException(e); + } + + while (true) { + try (Connection connection = DriverManager.getConnection(url, username, password)) { + System.out.println("--------------------------------> DB OK <------------------------------------"); + break; + } catch (SQLException e) { + System.out.println("------------------------> DB not ready - waiting <---------------------------"); + System.out.print(" url: "); + System.out.println(this.getUrl()); + System.out.print(" username: "); + System.out.println(this.getUsername()); + System.out.print(" driverClassName: "); + System.out.println(this.getDriverClassName()); + try { + Thread.sleep(1000l); + } catch (InterruptedException e1) { + } + } + } + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getUsername() { + return username; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getPassword() { + return password; + } + + public void setDriverClassName(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getDriverClassName() { + return driverClassName; + } +} diff --git a/commons/src/main/java/org/georchestra/commons/configuration/GeorchestraConfiguration.java b/commons/src/main/java/org/georchestra/commons/configuration/GeorchestraConfiguration.java index 2d3f7ab217..ac33c6fe63 100644 --- a/commons/src/main/java/org/georchestra/commons/configuration/GeorchestraConfiguration.java +++ b/commons/src/main/java/org/georchestra/commons/configuration/GeorchestraConfiguration.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + package org.georchestra.commons.configuration; import java.io.File; @@ -15,12 +34,8 @@ import org.apache.commons.io.IOUtils; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.DOMConfigurator; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.context.ServletContextAware; -@Controller -public class GeorchestraConfiguration implements ServletContextAware { +public class GeorchestraConfiguration { protected String globalDatadir; protected String contextDataDir; @@ -28,45 +43,28 @@ public class GeorchestraConfiguration implements ServletContextAware { protected String contextName; protected ServletContext ctx; - protected Properties applicationSpecificProperties = new Properties(); - public String getContextDataDir() { return contextDataDir; } private boolean activated = false; - public void init() {} + public void init() { + } public GeorchestraConfiguration(String context) { this.contextName = context; globalDatadir = System.getProperty("georchestra.datadir"); if (globalDatadir != null) { - contextDataDir = new File(String.format("%s%s%s%s", globalDatadir, File.separator, context, File.separator)).getAbsolutePath(); + contextDataDir = new File(String.format("%s%s%s%s", globalDatadir, File.separator, context, File.separator)) + .getAbsolutePath(); // Simple check that the path exists if (new File(contextDataDir).exists() == false) { contextDataDir = null; return; } - // loads the application context property file - FileInputStream propsFis = null; - try { - try { - // application-context - propsFis = new FileInputStream(new File(contextDataDir, context + ".properties")); - InputStreamReader isr = new InputStreamReader(propsFis, "UTF8"); - applicationSpecificProperties.load(isr); - } finally { - if (propsFis != null) { - propsFis.close(); - } - } - } catch (Exception e) { - activated = false; - return; - } // log4j configuration File log4jProperties = new File(contextDataDir, "log4j" + File.separator + "log4j.properties"); @@ -92,10 +90,14 @@ public GeorchestraConfiguration(String context) { * @throws IOException */ public Properties loadCustomPropertiesFile(String key) throws IOException { + return this.loadPropertiesFile(new File(contextDataDir, key + ".properties")); + } + + private Properties loadPropertiesFile(File path) throws IOException { Properties prop = new Properties(); FileInputStream fisProp = null; try { - fisProp = new FileInputStream(new File(contextDataDir, key + ".properties")); + fisProp = new FileInputStream(path); InputStreamReader isrProp = new InputStreamReader(fisProp, "UTF8"); prop.load(isrProp); } finally { @@ -106,27 +108,21 @@ public Properties loadCustomPropertiesFile(String key) throws IOException { return prop; } - public String getProperty(String key) { - if ((applicationSpecificProperties == null) || (activated == false)) - return null; - return applicationSpecificProperties.getProperty(key); - } - public boolean activated() { return activated; } /** - * This controller allows to intercept GEOR_custom.js used - * in Mapfishapp and Extractorapp. + * This method generate GEOR_custom.js used in Mapfishapp, Extractorapp and + * Analytics. + * * @param request * @param response * @throws Exception */ - @RequestMapping(value= "/app/js/GEOR_custom.js") public void getGeorCustom(HttpServletRequest request, HttpServletResponse response) throws Exception { - response.setContentType("application/javascript; charset=UTF-8"); + response.setContentType("application/javascript"); // we could get extra infos from the DB or elsewhere, and // add them to the variables provided by the js file :-) if (contextDataDir != null) { @@ -137,15 +133,9 @@ public void getGeorCustom(HttpServletRequest request, HttpServletResponse respon } } // Fallback on the default one provided by the webapp - InputStream is = this.ctx.getResourceAsStream("/app/js/GEOR_custom.js"); + InputStream is = request.getSession().getServletContext().getResourceAsStream("/app/js/GEOR_custom.js"); byte[] content = IOUtils.toByteArray(is); response.getOutputStream().write(content); return; } - - @Override - public void setServletContext(ServletContext servletContext) { - this.ctx = servletContext; - - } } diff --git a/commons/src/main/java/org/georchestra/commons/security/SecurityHeaders.java b/commons/src/main/java/org/georchestra/commons/security/SecurityHeaders.java new file mode 100644 index 0000000000..229e6fbdb5 --- /dev/null +++ b/commons/src/main/java/org/georchestra/commons/security/SecurityHeaders.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.commons.security; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.georchestra.security.model.GeorchestraUser; +import org.georchestra.security.model.Organization; + +/** + * A collection of header names commonly used by the security-proxy gateway + * application + * + * @author Jesse on 5/5/2014. + */ +public class SecurityHeaders { + // well-known header names + public static final String SEC_PROXY = "sec-proxy"; + + /** + * Used to send the full {@link GeorchestraUser user details} as a Base64 + * encoded JSON string + */ + public static final String SEC_USER = "sec-user"; + /** + * Used to send the full {@link Organization} as a Base64 encoded JSON string + */ + public static final String SEC_ORGANIZATION = "sec-organization"; + + public static final String SEC_USERID = "sec-userid"; + public static final String SEC_LASTUPDATED = "sec-lastupdated"; + public static final String SEC_USERNAME = "sec-username"; + public static final String SEC_ROLES = "sec-roles"; + public static final String SEC_FIRSTNAME = "sec-firstname"; + public static final String SEC_LASTNAME = "sec-lastname"; + public static final String SEC_EMAIL = "sec-email"; + public static final String SEC_TEL = "sec-tel"; + + public static final String SEC_ORG = "sec-org"; + public static final String SEC_ORGID = "sec-orgid"; + public static final String SEC_ORGNAME = "sec-orgname"; + public static final String SEC_ORG_LASTUPDATED = "sec-org-lastupdated"; + public static final String IMP_ROLES = "imp-roles"; + public static final String IMP_USERNAME = "imp-username"; + + /** + * @return the decoded header value, if it contains multiple values separated by + * a comma, is the returned value + */ + public static String decode(final String headerValue) { + if (null == headerValue) { + return null; + } + final boolean isMultipleValues = headerValue.indexOf(',') > -1; + if (isMultipleValues) { + String[] singleValues = headerValue.split(",", -1);// -1 avoids omitting empty strings + String decoded = Stream.of(singleValues).map(SecurityHeaders::decodeSingle).filter(Objects::nonNull) + .collect(Collectors.joining(",")); + return decoded; + } + return decodeSingle(headerValue); + } + + /** + * @return all individual header values decoded; individual values are separated + * by a comma in the argument string + */ + public static List decodeAsList(String headerValue) { + if (null == headerValue) { + return null; + } + String[] singleValues = headerValue.split(",", -1);// -1 avoids omitting empty strings + List values = Stream.of(singleValues).map(SecurityHeaders::decode).collect(Collectors.toList()); + return values; + } + + private static String decodeSingle(String value) { + if (null == value) { + return null; + } + // very simple implementation, we only support base64 so far + if (value.startsWith("{base64}")) { + value = value.substring("{base64}".length()); + byte[] bytes = Base64.getDecoder().decode(value.getBytes(StandardCharsets.UTF_8)); + return new String(bytes, StandardCharsets.UTF_8); + } + return value; + } + + /** + * @return {base64} prefixed representations of all header values, + * separated by non encoded commas. e.g. + * {base64},{base64},...{base64} + */ + public static String encodeBase64(String... headerValues) { + if (headerValues == null) { + return null; + } + if (headerValues.length == 0) { + return ""; + } + String encoded; + if (headerValues.length == 1) { + encoded = encodeSingle(headerValues[0]); + } else { + encoded = Arrays.stream(headerValues).map(SecurityHeaders::encodeSingle).filter(Objects::nonNull) + .collect(Collectors.joining(",")); + } + return encoded; + } + + private static String encodeSingle(String value) { + if (value == null) { + return null; + } + if (value.isEmpty()) { + return ""; + } + String encoded = Base64.getEncoder().encodeToString(value.getBytes(StandardCharsets.UTF_8)); + return encoded.isEmpty() ? "" : "{base64}" + encoded; + } +} diff --git a/commons/src/main/java/org/georchestra/security/api/OrganizationsApi.java b/commons/src/main/java/org/georchestra/security/api/OrganizationsApi.java new file mode 100644 index 0000000000..addaa105ed --- /dev/null +++ b/commons/src/main/java/org/georchestra/security/api/OrganizationsApi.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ +package org.georchestra.security.api; + +import java.util.List; +import java.util.Optional; + +import org.georchestra.security.model.Organization; + +public interface OrganizationsApi { + + List findAll(); + + Optional findById(String id); + + Optional findByShortName(String shortName); +} diff --git a/commons/src/main/java/org/georchestra/security/api/RolesApi.java b/commons/src/main/java/org/georchestra/security/api/RolesApi.java new file mode 100644 index 0000000000..7eb5726e27 --- /dev/null +++ b/commons/src/main/java/org/georchestra/security/api/RolesApi.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ +package org.georchestra.security.api; + +import java.util.List; +import java.util.Optional; + +import org.georchestra.security.model.Role; + +public interface RolesApi { + + /** + * Find a user by {@link Role#getName() name} + */ + Optional findByName(String name); + + List findAll(); +} diff --git a/commons/src/main/java/org/georchestra/security/api/UsersApi.java b/commons/src/main/java/org/georchestra/security/api/UsersApi.java new file mode 100644 index 0000000000..ca6d1c8e0b --- /dev/null +++ b/commons/src/main/java/org/georchestra/security/api/UsersApi.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ +package org.georchestra.security.api; + +import java.util.List; +import java.util.Optional; + +import org.georchestra.security.model.GeorchestraUser; + +public interface UsersApi { + + /** + * Find a user by {@link GeorchestraUser#getId() unique identifier} + */ + Optional findById(String id); + + /** + * Find a user by {@link GeorchestraUser#getUsername() login name} + */ + Optional findByUsername(String username); + + List findAll(); +} diff --git a/commons/src/main/java/org/georchestra/security/model/GeorchestraUser.java b/commons/src/main/java/org/georchestra/security/model/GeorchestraUser.java new file mode 100644 index 0000000000..ab3e8a6b0b --- /dev/null +++ b/commons/src/main/java/org/georchestra/security/model/GeorchestraUser.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ +package org.georchestra.security.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class GeorchestraUser implements Serializable { + private static final long serialVersionUID = -1; + + /////// Default mandatory properties. ///// + /////// Some optional properties may be made mandatory on a per-application + /////// basis ///// + + /** Provided by request header {@code sec-username} */ + private String username; + + /** Provided by request header {@code sec-roles} */ + private List roles = new ArrayList<>(); + + /** + * User's organization short name. Provided by request header {@code sec-org}, + * legacy way of identifying by LDAP's {@code org.cn} attribute, which may + * change over time + */ + private String organization; + + /////// Default optional properties. ///// + /////// Some may be made mandatory on a per-application basis ///// + + /** Provided by request header {@code sec-userid} */ + private String id; + + /** + * String that somehow represents the current version, may be a timestamp, a + * hash, etc. Provided by request header {@code sec-lastupdated} + */ + private String lastUpdated; + + /** Provided by request header {@code sec-firstname} */ + private String firstName; + + /** Provided by request header {@code sec-lastname} */ + private String lastName; + + /** Provided by request header {@code sec-email} */ + private String email; + + /** Provided by request header {@code sec-address} */ + private String postalAddress; + + /** Provided by request header {@code sec-tel} */ + private String telephoneNumber; + + /** Provided by request header {@code sec-title} */ + private String title; + + /** Provided by request header {@code sec-notes} */ + private String notes; + + public void setRoles(List roles) { + this.roles = roles == null ? new ArrayList<>() : roles; + } +} diff --git a/commons/src/main/java/org/georchestra/security/model/GeorchestraUserHasher.java b/commons/src/main/java/org/georchestra/security/model/GeorchestraUserHasher.java new file mode 100644 index 0000000000..c8b94da064 --- /dev/null +++ b/commons/src/main/java/org/georchestra/security/model/GeorchestraUserHasher.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ +package org.georchestra.security.model; + +import java.util.List; +import java.util.Objects; + +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; + +/** + * Utility to create a stable hash for a {@link GeorchestraUser}, + * {@link Organization}, and {@link Role} objects, that can be used as their + * {@code lastUpdated} property, in order to quickly compare whether a given + * instance matches a previously seen version, where the system may save the + * previously seen "hash" but not the user instance itself. + */ +public class GeorchestraUserHasher { + + public static String createHash(GeorchestraUser user) { + Hasher hasher = Hashing.sha256().newHasher(); + hasher.putUnencodedChars(nonNull(user.getId())); + hasher.putUnencodedChars(nonNull(user.getUsername())); + hasher.putUnencodedChars(nonNull(user.getFirstName())); + hasher.putUnencodedChars(nonNull(user.getLastName())); + hasher.putUnencodedChars(nonNull(user.getOrganization())); + hasher.putUnencodedChars(nonNull(user.getEmail())); + hasher.putUnencodedChars(nonNull(user.getNotes())); + hasher.putUnencodedChars(nonNull(user.getPostalAddress())); + hasher.putUnencodedChars(nonNull(user.getTelephoneNumber())); + hasher.putUnencodedChars(nonNull(user.getTitle())); + hashList(user.getRoles(), hasher); + + String hexHash = hasher.hash().toString(); + return hexHash; + } + + public static String createHash(Organization organization) { + Hasher hasher = Hashing.sha256().newHasher(); + hashOrg(organization, hasher); + String hexHash = hasher.hash().toString(); + return hexHash; + } + + public static String createHash(Role role) { + Hasher hasher = Hashing.sha256().newHasher(); + hasher.putUnencodedChars(nonNull(role.getId())); + hasher.putUnencodedChars(nonNull(role.getName())); + hasher.putUnencodedChars(nonNull(role.getDescription())); + hashList(role.getMembers(), hasher); + String hexHash = hasher.hash().toString(); + return hexHash; + } + + private static void hashOrg(Organization org, Hasher hasher) { + hasher.putUnencodedChars("org"); + if (null != org) { + hasher.putUnencodedChars(nonNull(org.getId())); + hasher.putUnencodedChars(nonNull(org.getShortName())); + hasher.putUnencodedChars(nonNull(org.getName())); + hasher.putUnencodedChars(nonNull(org.getLastUpdated())); + hasher.putUnencodedChars(nonNull(org.getCategory())); + hasher.putUnencodedChars(nonNull(org.getDescription())); + hasher.putUnencodedChars(nonNull(org.getLinkage())); + hasher.putUnencodedChars(nonNull(org.getNotes())); + hashList(org.getMembers(), hasher); + } + } + + private static CharSequence nonNull(String s) { + return s == null ? "" : s; + } + + private static void hashList(List list, Hasher hasher) { + if (list != null) { + list.stream().filter(Objects::nonNull).sorted().forEach(role -> hasher.putUnencodedChars(nonNull(role))); + } + } + +} diff --git a/commons/src/main/java/org/georchestra/security/model/Organization.java b/commons/src/main/java/org/georchestra/security/model/Organization.java new file mode 100644 index 0000000000..e8eef3f24c --- /dev/null +++ b/commons/src/main/java/org/georchestra/security/model/Organization.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ +package org.georchestra.security.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +@Data +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@With +public class Organization implements Serializable { + private static final long serialVersionUID = -1; + + /** Provided by request header {@code sec-orgid}, usually stable UUID */ + private String id; + + /** + * Provided by request header {@code sec-org}, legacy way of identifying by + * LDAP's {@code org.cn} attribute, which may change over time + */ + private String shortName; + + /** + * Provided by request header {@code sec-orgname}, due to legacy LDAP mapping + * {@code sec-orgname=org.o} + */ + private String name; + + /** Provided by request header {@code sec-org-linkage} */ + private String linkage; + + /** Provided by request header {@code sec-org-address} */ + private String postalAddress; + + /** Provided by request header {@code sec-org-category} */ + private String category; + + /** Provided by request header {@code sec-org-description} */ + private String description; + + /** Provided by request header {@code sec-org-notes} */ + private String notes; + + /** + * String that somehow represents the current version, may be a timestamp, a + * hash, etc. Provided by request header {@code sec-lastupdated} + */ + private String lastUpdated; + + /** + * List of {@link GeorchestraUser#getUsername() user names} that belong to this + * organization + */ + private List members = new ArrayList<>(); + + public void setMembers(List members) { + this.members = members == null ? new ArrayList<>() : members; + } +} \ No newline at end of file diff --git a/commons/src/main/java/org/georchestra/security/model/Role.java b/commons/src/main/java/org/georchestra/security/model/Role.java new file mode 100644 index 0000000000..418534d889 --- /dev/null +++ b/commons/src/main/java/org/georchestra/security/model/Role.java @@ -0,0 +1,42 @@ +package org.georchestra.security.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import lombok.Data; + +public @Data class Role implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * Uniquely identifies a role. Format could be UUID, but it's at the discretion + * of the provider + */ + private String id; + /** + * Role name, provides a unique identity for the role, but can mutate over time + */ + private String name; + /** + * Role intended purpose + */ + private String description; + + /** + * String that somehow represents the current version, may be a timestamp, a + * hash, etc. Provided by request header {@code sec-lastupdated} + */ + private String lastUpdated; + + /** + * List of {@link GeorchestraUser#getUsername() user names} that belong to this + * role + */ + private List members = new ArrayList<>(); + + public void setMembers(List members) { + this.members = members == null ? new ArrayList<>() : members; + } + +} diff --git a/commons/src/test/java/org/georchestra/commons/security/SecurityHeadersTest.java b/commons/src/test/java/org/georchestra/commons/security/SecurityHeadersTest.java new file mode 100644 index 0000000000..dec8b5dea5 --- /dev/null +++ b/commons/src/test/java/org/georchestra/commons/security/SecurityHeadersTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2021 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ +package org.georchestra.commons.security; + +import static java.util.Arrays.asList; +import static org.georchestra.commons.security.SecurityHeaders.decode; +import static org.georchestra.commons.security.SecurityHeaders.decodeAsList; +import static org.georchestra.commons.security.SecurityHeaders.encodeBase64; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.List; + +import org.junit.Test; + +public class SecurityHeadersTest { + + @Test + public void encodeBase64_Null() { + assertNull(encodeBase64((String) null)); + } + + @Test + public void encodeBase64_Japanese() { + final String givenName = "ガブリエル"; + final String lastName = "ロルダン"; + + final String encodedGivenName = "44Ks44OW44Oq44Ko44Or"; + final String encodedLastName = "44Ot44Or44OA44Oz"; + + assertEquals("{base64}" + encodedGivenName, encodeBase64(givenName)); + assertEquals("{base64}" + encodedLastName, encodeBase64(lastName)); + } + + @Test + public void encodeBase64_Empty() { + assertEquals("empty string should remain empty string", "", encodeBase64("")); + assertEquals("", decode("")); + } + + @Test + public void encodeBase64_Space() { + assertEquals("{base64}IA==", encodeBase64(" ")); + assertEquals(" ", decode("{base64}IA==")); + } + + @Test + public void encodeBase64_Spaces() { + assertEquals("{base64}ICAgICAgICAgIA==", encodeBase64(" ")); + assertEquals(" ", decode("{base64}ICAgICAgICAgIA==")); + } + + @Test + public void encodeBase64_PrefixString() { + assertEquals("{base64}e2Jhc2U2NH0=", encodeBase64("{base64}")); + assertEquals("{base64}", decode("{base64}e2Jhc2U2NH0=")); + } + + @Test + public void encodeBase64_Newlines() { + assertEquals("{base64}dGV4dAp3aXRoDQoKZXdsaW5lcwoK", encodeBase64("text\nwith\r\n\newlines\n\n")); + assertEquals("text\nwith\r\n\newlines\n\n", decode("{base64}dGV4dAp3aXRoDQoKZXdsaW5lcwoK")); + } + + @Test + public void testDecodeNull() { + assertNull(decode(null)); + } + + @Test + public void testDecodeEmpty() { + assertEquals("", decode("")); + } + + @Test + public void testDecode_NoEncoding() { + final String givenName = "ガブリエル"; + assertEquals(givenName, decode(givenName)); + assertEquals("{}" + givenName, decode("{}" + givenName)); + assertEquals("{" + givenName + "}", decode("{" + givenName + "}")); + } + + @Test + public void testDecode_Base64() { + final String givenName = "ガブリエル"; + final String lastName = "ロルダン"; + + assertEquals(givenName, decode(encodeBase64(givenName))); + assertEquals(lastName, decode(encodeBase64(lastName))); + + assertEquals("{}" + givenName, decode(encodeBase64("{}" + givenName))); + assertEquals("{" + lastName + "}", decode(encodeBase64("{" + lastName + "}"))); + } + + @Test + public void testMultipleValues() { + final String givenName = "ガブリエル"; + final String lastName = "ロルダン"; + // a header value with a comma itself should always be encoded, up to the user + // if it's not, by HTTP spec, multiple header values shall be comma separated + final String fullName = lastName + ", " + givenName; + + String encoded = encodeBase64(givenName, lastName, fullName); + String decoded = decode(encoded); + assertEquals(String.format("%s,%s,%s", givenName, lastName, fullName), decoded); + + List values = decodeAsList(encoded); + assertEquals(3, values.size()); + assertEquals(givenName, values.get(0)); + assertEquals(lastName, values.get(1)); + assertEquals(fullName, values.get(2)); + } + + @Test + public void testMultipleValuesWithEmptyElementsPreserved() { + final String givenName = "ガブリエル"; + final String lastName = "ロルダン"; + + String encoded = encodeBase64("", givenName, "", lastName, ""); + String decoded = decode(encoded); + assertEquals(String.format(",%s,,%s,", givenName, lastName), decoded); + + List values = decodeAsList(encoded); + assertEquals(5, values.size()); + assertEquals("", values.get(0)); + assertEquals(givenName, values.get(1)); + assertEquals("", values.get(2)); + assertEquals(lastName, values.get(3)); + assertEquals("", values.get(4)); + } + + @Test + public void testDecodeMultipleValuesNoEncoding() { + assertEquals("v1,V2,v3", decode("v1,V2,v3")); + assertEquals(",v1,,V2,,v3,", decode(",v1,,V2,,v3,")); + assertEquals(",v1,,V2,,v3,,,", decode(",v1,,V2,,v3,,,")); + assertEquals(",,,,", decode(",,,,")); + + assertEquals(asList("v1", "V2", "v3"), decodeAsList("v1,V2,v3")); + assertEquals(asList("", "v1", "", "V2", "", "v3", ""), decodeAsList(",v1,,V2,,v3,")); + assertEquals(asList("", "", ""), decodeAsList(",,")); + } +} diff --git a/config/README.md b/config/README.md deleted file mode 100644 index 1baaf4169f..0000000000 --- a/config/README.md +++ /dev/null @@ -1,516 +0,0 @@ -Config -====== - -Configuration in geOrchestra is designed to reduce the amount of work required to maintain a full configuration for all modules as much as possible at the same time allowing for complete customisation. - -The rough concept is that a configuration jar is created containing sub-directories corresponding to the maven modules in geOrchestra. The files in the configuration will overwrite the files in the module (or add to module if file does not exist). - -However, there are several problems with this solution. For example if a file is large and only a couple lines need to be changed it makes more sense to only update those lines. The second problem is that many projects have shared configuration parameters like database urls. - -There are two parts that are designed to overcome these problems: - - * The **shared.maven.filters** properties file in the root config directory is a file called *shared.maven.filters*. This file contains the parameters that are shared between many projects. The file contains the default values and when the configuration jar is being built; all text files with @propertyName@ will be replaced with the property in *shared.maven.filters* based on *propertyName*. - * To provide further flexibility, configurations can have a script in the *build_support* directory called *GenerateConfig.groovy*. [Groovy](http://groovy.codehaus.org/) is a scripting language based on Java syntax, but has several useful features to make writing scripts easier. - -Configuration Build Process -=========================== -Suppose the maven command: - -mvn install -Dserver=myproj -Dsub.target=test - - 1. Build config module - 1. Execute the configuration/myproj/build_support/GenerateConfig.groovy script - * **Note:** the value test (value of sub.target property) is passed to GenerateConfig.groovy as the subTarget parameter - 2. copy files from [default, configuration/myproj/ and target/generate] to target/classes - * All text files are processed and all @propertyName@ tags are replaced by properties loaded from (top has priority): - * target/generated/shared.maven.filters - * configuration/myproj/build_support/shared.maven.filters - * config/shared.maven.filters - * Note: build_support directory is not copied - 3. The files in target/classes are bundled up as a jar and installed in the local repository - 2. Build the other modules in geOrchestra - 1. in the maven prepare-resources phase that unpacks the config jar into the modules target directory - 2. in the maven copy-resources phase, the files in src/main/filtered-resources and target/conf/ are copied and processed using the filters in target/conf//maven.filters - 3. Normal maven processes continue - -Module Components -================= - -- config - - shared.maven.filters - - defaults - contains configuration settings and default branding - - DeployScript.groovy - the default deploy script - - each sub-directory is a name of one of the geOrchestra modules. The purpose of each sub-directory is to override files in the actual project module refer to. For example the file security-proxy/WEB-INF/classes/log4j.properties in the defaults folder will overwrite the WEB-INF/classes/log4j.properties file in the security proxy war if it exists. If the file does not exist, the file will be added to the war. Note: this is a very convenient place for config files which should be shipped with the project, but which are meant to be overridden by instance specific files. - - configuration - contains all the configurations that can be built by configuration module - - - directory containing all files that differ from the defaults for a particular target platform. the name of the directory matches the server java property. (mvn -Dserver=config for example) - - build_support - special directory that is *NOT* copied to the config - - GenerateConfig.groovy - Script for creating/copying configuration files. - - shared.maven.filters - Properties referenced by the main shared.maven.filters or properties that will override the main share.maven.filter properties - - src - contains [Groovy](http://groovy.codehaus.org/) files for helping implement *GenerateConfig.groovy* scripts (see below) - -shared.maven.filters -==================== - -The *shared.maven.filters* properties file in the root config directory is a file called *shared.maven.filters*. This file contains the parameters that are shared between many projects. The file contains the default values and when the configuration jar is being built; all text files with @propertyName@ will be replaced with the property in *shared.maven.filters* based on *propertyName*. - -In addition to the *shared.maven.filters* each configuration has to have a *shared.maven.filters* in its build_support directory as well. At minimum these files must have: - - * shared.privileged.geoserver.user - * shared.privileged.geoserver - * shared.ldap.admin.password - * shared.server.name - * application_home - -It may also override any properties in the default *shared.maven.filters* and add new properties specific to this configuration - -A final *shared.maven.filters* can be generated by the *GenerateConfig.groovy* file to the target/generated directory. This properties in this file take precedence over both other files. - -GenerateConfig.groovy -===================== - -Each config can contain a GenerateConfig.groovy file which can generate config files into the target/generated directory. The files in target/generated take precedence over all other configuration files. - -This script has two purposes: - - 1. The purpose of this script is to allow maximum reuse of the default configuration files - 2. Allow a single configuration directory to be used for test, integration and production servers. - -The way that these scripts can satisfy these two purposes is by reading the base configuration file (be it in defaults, config or the basic project module) modifying it and saving it to the target/generated directory. - -Consider a couple of simple examples. - -Example 1 - Single configuration - Multiple target servers: ---------------------------------------------------------- - -Suppose the test server of a project had one public url and the production server had another. One might put the public url in the configuration's *shared.maven.filters* file and the GenerateConfig.groovy file will check the subTarget parameter and when the parameter is 'test' (or whatever value the developer chooses) the script will create a new *shared.maven.filters* in the target/generated (passed to script as the outputDir parameter). Since the *shared.maven.filters* in the target/generated directory has highest precedence it effectively overrides the production value with the test server value. - - class GenerateConfig { - def generate(def project, def log, def ant, def basedirFile, - def target, def subTarget, def targetDir, - def buildSupportDir, def outputDir) { - def host = "shared.server.name=" - switch (subTarget) { - case "test": - host += "integration.host.com" - break - default: - host += "production.host.com" - break - } - new File(outputDir, 'shared.maven.filters') << host - } - } - -Example 2 - Change single property in a default maven.filters file: -------------------------------------------------------------------- - -Suppose for a particular application the extraction files should all start with the *proj-extract-* prefix instead of the *extraction-* prefix which is in the defaults/extractorapp/maven.filters folder. The maven.filters files don't have an override mechanism like shared.maven.filters thus the entire maven.filters must be in the config even if only 1 value is modified. This is bad for a maintenance point of view because if the defaults has a new value added of one of the defaults changes the new application will not get that change. It is better to only make the single change. - -In this case instead of copying the entire maven.filters file one could use the PropertyUpdater class to copy the default maven.filters file updating only the single property. Thanks to the PropertyUpdater this process is simple. - - class GenerateConfig { - def generate(def project, def log, def ant, def basedirFile, - def target, def subTarget, def targetDir, - def buildSupportDir, def outputDir) { - new PropertyUpdate( - path: 'maven.filter', - from: 'defaults/extractorapp', - to: 'extractorapp').update { properties -> - properties['extractionFolderPrefix'] = 'proj-extract-' - } - } - } - - -Example 3 - Change value in an xml file part of extractorapp module: --------------------------------------------------------------------- - -Suppose that the urlrewrite.xml file in the extractorapp module needs to be modified for the project. The obvious way would be to copy the file to the configuration/project/extractorapp/WEB-INF directory and modify it. This has the same problem as Example 2, it overwrites the original completely. Another way would be to use scripting to modify the file and write the updated file to target/generated/extractorapp/WEB-INF. - - class GenerateConfig { - def generate(def project, def log, def ant, def basedirFile, - def target, def subTarget, def targetDir, - def buildSupportDir, def outputDir) { - new XmlUpdate( - path: 'WEB-INF/urlrewrite.xml', - fromProject: 'extractorapp', - from: 'src/main/webapp/WEB-INF', - to: outputDir+'/extractorapp').update { originalXml -> - originalXml. - } - } - } - -Script Writing Resources ------------------------- - -> The scripting language used for in the GenerateConfig.groovy is the [Groovy](http://groovy.codehaus.org/) programming language. It is based on the Java language and most java syntax will work in Groovy as well. But Groovy is dynamic and has several conveniences that make it better for scripting than java. It is pretty easy to use Google to find information about Groovy but hopefully the examples provided in this document and the Javadocs will provide a good introduction to the most common tasks needed. - -'''Note:''' Javadocs are generated when the config module is built and can be viewed in config\target\site\apidocs\index.html - -### Create new file -This example shows one way of creating file objects and writing to the file - - // assign file system path separator to variable - def S = File.separator - // create a new file. Use Groovy's string interpolation to put the correct path separator in path - def outputFile = new File(outputDir, "geotwork-main${S}webapp${S}WEB-INF${S}newFile") - // write some text to the file - outputFile << "text of new file" - -### Copy a file -This example shows one way of creating file objects and copying the contents of one file to the other file - - // assign file system path separator to variable - def S = File.separator - // use replace to change / to correct platform separator and use as keyword to change string to a file - def outputFile = outputDir+"/geonetwork-main/webapp/WEB-INF/newFile".replace('/',S) as File.class - def inputFile = basedirFile+"/../geonetwork-main/webapp/WEB-INF/log4j.cfg".replace('/',S) as File.class - // copy one file to the other - outputFile << inputFile.getBytes() - -### Set class fields during construction - -A common pattern used in the support classes of config is a syntax in Groovy where class fields can be set during construction. Consider the following groovy class: - - class GroovyClass { - def field1 - def field2 - } - -(Note: def field1 is the same as Object field1) - -There are no constructors defined but one can create an instance and set the properties in a single declaration: - - new GroovyClass( field1: 'field1', field2: 'field2') - -or if one only wants to assign a single field: - - new GroovyClass(field1: 'field') - -### Collections in Groovy - -Collection objects in Groovy have special syntax to make them easier to work with: - -Maps: - - // create map - def map = ['key1': 'value1', 'key2': 'value2'] - // update map - map['key1'] = 'newValue' - // maps are java.util.Map objects so those methods apply: - map.remove('key1') - -List: - - // create a list. Result Type is java.util.List - def list = ['value1', 'value2'] - // short hand to add new value - list << 'newValue' - // normal java.util.List method to add many - list.addAll( ['nv1', 'nv2'] ) - // Access an element in list - list[2] - -For more on collections in groovy see: http://groovy.codehaus.org/Collections - -### AbstractUpdate - -Many of the support classes extend AbstractUpdate since it provides several ways of specifying the input and output of an update process. For clarity, when I update I always mean load an input file, modify it and save it to the target/generated directory. In no cases should the original file be modified. - -The primary responsibility of AbstractUpdate is to provide convenience methods: getFromFile and getToFile for the subclasses based on the parameters. - - new PropertyUpdate ( - projectPath: 'geonetwork', // projectPath indicates the file is in the /geonetwork directory, not a config subdirectory - path: 'webapp/WEB-INF/spring.xml'. // path is used to determing both to and from. - to: 'geonetwork-main', // the base of the to file (relative to target/generated). The path will be appended to the to field. - from: 'geonetwork-main/src/main' // the final from file is projectPath/from/path - ).update { properties -> /* update properties */} - -In many cases only 'to' and 'from' are required and even some subclasses of AbstractUpdate (like PropertyUpdate) only requires the 'to' field. Although it is usually beneficial to define the 'path' field so that it doesn't need to be repeated in both 'from' and 'to' fields. - -### PropertyUpdate -This example shows how to update (or create) a properties file using the PropertyUpdate support class. - - 1. The maven.filter file is loaded into memory - 2. 4 properties are updated or added to the properties object - 3. the properties are written to target/generated/security-proxy/maven.filter - -Example Code: - - new PropertyUpdate( - path: 'maven.filter', - from: 'defaults/security-proxy', - to: 'security-proxy').update { properties -> - properties['shared.server.name'] = host - properties['shared.default.log.level'] = logLevel - properties['application_home'] = applicationHome - properties['shared.ldapUrl'] = ldapUrl - } - -### XmlUpdate -This first example shows how to generate an xml file based on an existing xml file. - - 1. The file is loaded into memory - 2. All category elements are found - 3. findAll is used to find the category elements with the class attribute that contains the gn string - 4. geor is added to each class attribute in the elements found in the previous step - 5. the updated xml is written to target/generated/security-proxy/file.xml - -Example Code: - - new XmlUpdate( - path: 'file.xml', - from: 'defaults/security-proxy', - to: 'security-proxy').update { xml -> - xml.category.findAll {it.@class.contains("gn")}. each {cat -> - cat.@class = s.@class + " geor" // add new class to element - } - } - -See http://groovy.codehaus.org/Reading+XML+using+Groovy%27s+XmlParser for more details on how to update the xml - -This second example shows how to create a new xml file. - - 1. An XmlBuilder is created - 2. The builder is passed to the closure - 3. The closure constructs the xml: - 1. A config element is created (with no attributes) - 2. An import element is created as child of config. The import element has a file attribute and no children - 3. A bean element is created as a child of config. This element has children - 4. Etc... - 4. The xml is written to target/generated/security-proxy/file.xml - -Example Code: - - new XmlUpdate( - path: 'file.xml', - to: 'security-proxy').write { builder -> - builder.config() { - import(file: 'importFile.xml') - bean (id:'newbean', class: 'org.georchestra.Bean') { - property (key: 'property', value: 'value') - } - } - } - -See http://groovy.codehaus.org/Creating+XML+using+Groovy%27s+MarkupBuilder for more details on how to construct xml documents with the Groovy MarkupBuilder. - -### TextUpdate - -The text update class assists in updating raw text file by searching for occurances of regular expressions and replacing the matched section with the new text. This example also illustrates how one can take the text from a geOrchestra module (in this case Geonetwork) and update that text. - - 1. Load /geonetwork/web-client/src/main/resources/apps/georchestra/js/Settings.js into memory - * Note: the from path is constructed from: // - 2. The pattern GeoNetwork\.Util\.defaultLocale\s*=\s*'eng' is replaced with "GeoNetwork.Util.defaultLocale = 'fre'" - * Note: List Javascript the /.../ indicates a regular expression. - * Note: Currently all matches of the regular expression are replaced - 3. The text is written out to target/generated/geonetwork-client/apps/georchestra/js/Settings.js - -Example Code: - - new TextUpdate( - path: 'apps/georchestra/js/Settings.js', - fromProject: "geonetwork", - from: 'web-client/src/main/resources/', - to: 'geonetwork-client/', - patternsToReplace: [ /GeoNetwork\.Util\.defaultLocale\s*=\s*'eng'/: "GeoNetwork.Util.defaultLocale = 'fre'"] - ).update() - -### MavenDownloader - -The maven downloader support class searches the repositories declared in the root pom.xml and the config pom.xml to locate Maven artifacts and download them. - -The following example downloads a single jar to target/generated/geoserver-webapp/WEB-INF/lib. - - new MavenDownloader( - artifact: ['com.vividsolutions','jts','1.13], - to: 'geoserver-webapp/WEB-INF/lib').download() - -One can also download several jars with one declaration by using the 'artifacts' field instead of the 'artifact' field. - - new MavenDownloader( - artifacts: [ - ['org.geoserver.extension','control-flow','2.2.4'], - ['com.vividsolutions','jts','1.13] - ], - to: 'geoserver-webapp/WEB-INF/lib').download() - -### FileSet - -A FileSet represents a set of files. It can be the files in a directory, a single file or all descendants of a directory. -If the file set contains several files a sort and a filter can be applied to the files - -Note: sorting the files requires loading all the files into memory and sorting. This is both slower and -requires more memory. - -Note: Sorting only applies to a single source. Not to all files in the file set. - -Examples: - - // Represents all js files that are descendants of - // $basedirFile/src/main/resources/georchestra/js - // all directories are recursively visited - new FileSet().descendants( - source:"$basedirFile/src/main/resources/georchestra/js", - filter:{ it.name.endsWith("*.js") } - ) - - // Represents a single file - new FileSet().file("App.js") - - // Represents the js files directly (not recursively) in the - // "web-client/src/main/resources/app/search/js" of the geonetwork project - // files are sorted by lastmodified date - new FileSet(project: "geonetwork").children( - source: "web-client/src/main/resources/app/search/js", - filter: {it.name.endsWith("*js")}, - sort: {o1, o2 -> o1.lastModified() - o2.lastModified} - ) - - // A fileset with first App.js then all js files in geonetwork directory - new FileSet(). - file("App.js"). - children( - source:"geonetwork", - filter: {it.name.endsWith(".js)} - ) - -The each method can be used to iterate through all the files and perform an action on each file in the FileSet - -### Minify - -The Minify class is a useful class for minifying either Javascript or CSS files into a single file. - -Example: - - new Minify( - sources: [ - new FileSet().descendants( - source:"$basedirFile/src/main/resources/georchestra/js", - filter:{ it.name.endsWith("*.js") } - ) - ], - output: "$targetDir/classes/apps/georchestra/js/Minified.js") - } - -### Execute an ant task - -Groovy provides a class called the [AntBuilder](http://groovy.codehaus.org/Using+Ant+from+Groovy). An instance is passed to the GenerateConfig class. The following example copies the config/configurations//build_support/geonetwork-main directory to /target/generated - - class GenerateConfig { - def generate(def project, def log, def ant, def basedirFile, - def target, def subTarget, def targetDir, - def buildSupportDir, def outputDir) { - ant.copy(todir: outputDir+"/geonetwork-main") { - fileset (dir: buildSupportDir+"/geonetwork-main") - } - } - } - -### Structured Scripting - -If the GenerateConfig script is complex it would likely be a good idea to structure the script in several classes and have GenerateConfig call those classes to do the work. One can even use packages like in java if one needs to. Although that is probably more than is typically needed. - -A common pattern used could be the following: - -GeoserverConfig.groovy: - - class GeoserverConfig { - def generate(Parameters params) { - // generate geoserver configuration file - } - } - -GeonetworkConfig.groovy: - - class GeonetworkConfig { - def generate(Parameters params) { - // generate geonetwork configuration file - } - } - -GenerateConfig.groovy - - class GenerateConfig { - def generate(def project, def log, def ant, def basedirFile, - def target, def subTarget, def targetDir, - def buildSupportDir, def outputDir) { - def params = Parameters.get - new GeoserverConfig().generate(params) - new GeonetworkConfig().generate(params) - } - } - -Property vs Profile -=================== - -When building the configuration module there are two Java system properties that are observed. - - * server - this property defines the directory in config/configurations to use as the configuration - * sub.target - this property is optional and is used if the same configuration is used for multiple target servers like test, integration, production. This property is really only used by GenerateConfig.groovy scripts - -One can specify them manually on the commandline: - - mvn install -Dserver=template -Dsub.target=test - -Or one can add a profile to /pom.xml that declares the properties when the profile is enabled. There are examples in the pom already that be be used as templates. The following example enables a profile: - - mvn install -Ptemplate - -See (http://maven.apache.org/guides/introduction/introduction-to-profiles.html) for more on maven profiles. - -Post Treatment Script -===================== - -Consider minification of javascript files in Geonetwork. In geonetwork, minification is done by Yui and the definitions are -in the pom.xml. As a result, a configuration cannot add files to be minified because maven will not recognize the changes. To overcome this limitation, -the geOrchestra build system will run a PostTreatment script if it is defined for that project. - -To declare a post treatment script, create a PostTreatment.groovy file in the project's configuration directory. - -For example, to define a Post Treatment script for geonetwork-client in a project "template". -Create the file: config/configurations/template/geonetwork-client/PostTreatment.groovy. This file should have the class: - - class PostTreatment { - def run(def project, def log, def ant, def basedirFile, def configDir, - def target, def subTarget, def targetDir) { - ... - } - } - -The file can also be generated and written to: conf/target/generated/geonetwork-client. - -These scripts will have access to the same classes the GenerateConfig scripts do. - -*Note*: Not all projects support post treatment scripts. Check the pom.xml for the project and check: - - * The gmaven plugin has been added to the project as follows: - - org.codehaus.groovy.maven - gmaven-plugin - - - ${project.groupId} - config - ${project.version} - - - - * The property _postTreatmentScript_ does not override the property defined in the root pom.xml. (Defining this property is a way to disable the post treatment script for projects that need the gmaven plugin but don't need the post treatment script execution) - -Since one of the more common tasks will be to add a minification step the following example illustrates how to do this. - - class PostTreatment { - def run(def project, def log, def ant, def basedirFile, def configDir, - def target, def subTarget, def targetDir) { - new Minify( - sources: [ - new FileSet().descendants( - source:"$basedirFile/src/main/resources/georchestra/js", - filter:{ it.name.endsWith("*.js") } - ) - ], - output: "$targetDir/classes/apps/georchestra/js/Minified.js") - } - } diff --git a/config/configurations/.gitignore b/config/configurations/.gitignore deleted file mode 100644 index da5efa24c1..0000000000 --- a/config/configurations/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!template -!localhost \ No newline at end of file diff --git a/config/configurations/localhost/build_support/GenerateConfig.groovy b/config/configurations/localhost/build_support/GenerateConfig.groovy deleted file mode 100644 index 0daa413cc7..0000000000 --- a/config/configurations/localhost/build_support/GenerateConfig.groovy +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file can optionally generate configuration files. The classic example - * is when a project has both a integration and a production server. - * - * The configuration my be in a subdirectory of build_support (which is not copied into the configuration by default) - * Depending on serverId, this script can copy the files to the outputDir and copy a shared.maven.filters with the parameters that - * are needed depending on serverId. More can be done but that is the classic example - */ -class GenerateConfig { - /** - * @param project The maven project. you can get all information about - * the project from this object - * @param log a logger for logging info - * @param ant an AntBuilder (see groovy docs) for executing ant tasks - * @param basedirFile a File object that references the base directory - * of the conf project - * @param target the server property which is normally set by the build - * profile. It indicates the project that is being built - * @param subTarget the "subTarget" that the project is being deployed - * to. For example integration or production - * @param targetDir a File object referencing the targetDir - * @param buildSupportDir a File object referencing the build_support - * dir of the target project - * @param outputDir the directory to copy the generated configuration - * files to - */ - - def generate(def project, def log, def ant, def basedirFile, - def target, def subTarget, def targetDir, - def buildSupportDir, def outputDir) { - // nothing needs to be done for this project - } -} diff --git a/config/configurations/localhost/build_support/shared.maven.filters b/config/configurations/localhost/build_support/shared.maven.filters deleted file mode 100644 index 2a8bc8327b..0000000000 --- a/config/configurations/localhost/build_support/shared.maven.filters +++ /dev/null @@ -1,193 +0,0 @@ -# build_support/shared.maven.filters -# -# This file is the primary configuration file for your SDI (spatial data infrastructure). -# It overrides the geOrchestra default parameters to suit your SDI environment. -# Secondary configuration files refers to shared.maven.filters parameters using the syntax -# @shared.[parameter.name]@ - -# -------------------------------------------------- -# PUBLIC URL -# -------------------------------------------------- - -# sets the public address and port of your SDI. -# + example + -# If your server is http://localhost/ on port 80 : -# shared.server.name=localhost -# shared.server.port=80 -# -shared.server.name=localhost -shared.server.port=8080 - -# -------------------------------------------------- -# LANGUAGE -# -------------------------------------------------- - -# sets the language for all modules -# May be one of the following: fr, es, en -# Default value: fr -# -shared.language=fr - -# -------------------------------------------------- -# LDAP -# -------------------------------------------------- - -# how geOrchestra should connect to the ldap directory -# + example + -# shared.ldap.host=localhost -# shared.ldap.port=389 - -shared.ldap.host=localhost -shared.ldap.port=33389 - -# shared.ldapUrl is automatically computed with the above -# shared.ldap parameters but you may override this setting -# -shared.ldapUrl=ldap://@shared.ldap.host@:@shared.ldap.port@ - - -# how geOrchestra should query the ldap directory. -# shared.ldap.baseDn is the top level of the directory tree -# shared.ldap.userSearchBaseDN is the query filter for users -# shared.ldap.groupSearchBaseDN is the query filter for groups -# shared.ldap.admin.dn is the query filter for the ldap admin user -# shared.ldap.admin.password is the ldap admin user password -# + example + -# shared.ldap.baseDn=dc=georchestra,dc=org -# shared.ldap.userSearchBaseDN=ou=users -# shared.ldap.groupSearchBaseDN=ou=groups -# shared.ldap.admin.dn=cn=admin,@shared.ldap.baseDn@ -# shared.ldap.admin.password=secret -# -shared.ldap.baseDn=dc=pigma,dc=org -shared.ldap.userSearchBaseDN=ou=users -shared.ldap.groupSearchBaseDN=ou=groups -shared.ldap.admin.dn=cn=admin,@shared.ldap.baseDn@ -shared.ldap.admin.password=secret - - - -# -------------------------------------------------- -# GEOSERVER -# -------------------------------------------------- - -# geOrchestra uses a privileged account to query geoserver -# with full admin privileges.This user MUST exist in the ldap -# directory and MUST belong to the ADMINISTRATOR group. -# + example + -# shared.privileged.geoserver.user=extractorapp_privileged_admin -# shared.privileged.geoserver.pass=gerlsSnFd6SmM -# -shared.privileged.geoserver.user=extractorapp_privileged_admin -shared.privileged.geoserver.pass=gerlsSnFd6SmM - - -# -------------------------------------------------- -# SMTP / Emails -# -------------------------------------------------- - -# geOrchestra send notification mails about pending -# and completed extraction requests. Use the following parameters -# according to your platform smtp gateway -# + example + -# shared.smtp.host=localhost -# shared.smtp.port=25 -# shared.email.replyTo=psc\@georchestra.org -# shared.email.from=psc\@georchestra.org -# -shared.smtp.host=localhost -shared.smtp.port=25 - -# for emails sent by extractorapp: -shared.email.replyTo=psc\@georchestra.org -shared.email.from=psc\@georchestra.org - -# the following email receives new account requests if ldapadmin -# is configured with moderatedSignup = true (which is the default) -shared.administrator.email=psc\@georchestra.org - -# send notification mails in HTML ? -# Defaults to: false (ie. plain text emails) -# If you change this value to true, you should also modify the emails -# templates in order to have full HTML emails. -shared.email.html=false - - - -# -------------------------------------------------- -# LOGFILES -# -------------------------------------------------- - -# geOrchestra redirects logs to the shared.log.dir directory. -# This directory must exist and must be writeable by the tomcat user -# + example + -# shared.log.dir=/var/log/tomcat8 -# -# Here we use /tmp because we want to prevent this kind of error -# http://applis-bretagne.fr/redmine/issues/4311 when Tomcat is not installed -# -shared.log.dir=e:/tmp - - -# -------------------------------------------------- -# DATABASES -# -------------------------------------------------- - -# geOrchestra stores some informations in several postgresql database. -# We assume that all databases are hosted on the same pg cluster -# but you may override this setting in the secondary configuration files. -# Make sure that the pg_hba.conf allows connections from the server -# hosting georchestra with the appropriate credentials -# + example + -# shared.psql.host=localhost -# shared.psql.port=5432 -# shared.psql.user=www-data -# shared.psql.pass=www-data -# -shared.psql.host=localhost -shared.psql.port=5432 -shared.psql.user=www-data -shared.psql.pass=www-data -shared.psql.url=psql://www-data:www-data@localhost/geonetwork - - -# -------------------------------------------------- -# DOWNLOAD FORM -# -------------------------------------------------- - -# If the downloadform feature is activated, -# useds will be asked to fill a form, read and accept an agreement -# (shared.download_form.pdfurl) prior to any download. -# The downloadform will log every download request in a database, -# see downloadform/sample/sample.sql -# + example + -# shared.download_form.activated=true -# shared.download_form.jdbcurl=jdbc:postgresql://@shared.psql.host@:@shared.psql.port@/downloadform -# shared.download_form.pdfurl=/_static/usage.pdf -# or -# shared.download_form.pdfurl=//url.to/cgu.pdf -# -shared.download_form.activated=true -shared.download_form.jdbcurl=jdbc:postgresql://@shared.psql.host@:@shared.psql.port@/downloadform -shared.download_form.pdfurl= - -# -------------------------------------------------- -# OGC statistics -# -------------------------------------------------- - -shared.ogc.statistics.activated=true - -# -------------------------------------------------- -# health check properties -# -------------------------------------------------- - -shared.psql.geonetwork.db=geonetwork - -# -------------------------------------------------- -# TO BE DOCUMENTED -# -------------------------------------------------- - -shared.tomcat.dir=/var/lib/tomcat8 -shared.apache.conf.dir=/dev/null - - diff --git a/config/configurations/template b/config/configurations/template deleted file mode 160000 index 81a6ab59df..0000000000 --- a/config/configurations/template +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 81a6ab59dfee4a1fcab2fb2a8ae69c77cbca18d3 diff --git a/config/defaults/analytics/WEB-INF/classes/log4j.properties b/config/defaults/analytics/WEB-INF/classes/log4j.properties deleted file mode 100644 index 23025cce6f..0000000000 --- a/config/defaults/analytics/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,12 +0,0 @@ -log4j.rootLogger=@shared.default.log.level@, R - -log4j.logger.org.georchestra.analytics=@shared.default.log.level@, R - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = @shared.log.dir@/analytics.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = @shared.log.dir@/analytics.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n - diff --git a/config/defaults/analytics/WEB-INF/jsp/header.jsp b/config/defaults/analytics/WEB-INF/jsp/header.jsp deleted file mode 100644 index ee03e19816..0000000000 --- a/config/defaults/analytics/WEB-INF/jsp/header.jsp +++ /dev/null @@ -1,11 +0,0 @@ -<%@ page pageEncoding="UTF-8"%> - - - - -
- - -
-
-
diff --git a/config/defaults/analytics/maven.filter b/config/defaults/analytics/maven.filter deleted file mode 100644 index 100798004d..0000000000 --- a/config/defaults/analytics/maven.filter +++ /dev/null @@ -1,8 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - -instance=@shared.instance.name@ -language=@shared.language@ - -dlJdbcUrlOGC=@shared.ogc.statistics.jdbcurl@?user=@shared.psql.user@&password=@shared.psql.pass@ -dlJdbcUrlDLForm=@shared.download_form.jdbcurl@?user=@shared.psql.user@&password=@shared.psql.pass@ diff --git a/config/defaults/cas-server-webapp/WEB-INF/classes/log4j.xml b/config/defaults/cas-server-webapp/WEB-INF/classes/log4j.xml deleted file mode 100644 index a7d549ce19..0000000000 --- a/config/defaults/cas-server-webapp/WEB-INF/classes/log4j.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/defaults/cas-server-webapp/maven.filter b/config/defaults/cas-server-webapp/maven.filter deleted file mode 100644 index d7e7da9337..0000000000 --- a/config/defaults/cas-server-webapp/maven.filter +++ /dev/null @@ -1,15 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - -host=@shared.server.name@ -port=@shared.server.port@ -ldapUrl=@shared.ldapUrl@ -groupSearchBaseDn=@shared.ldap.groupSearchBaseDN@,@shared.ldap.baseDn@ -userSearchBaseDn=@shared.ldap.userSearchBaseDN@,@shared.ldap.baseDn@ -userFilter=(@shared.ldap.uid@={user}) -ldapAdminUsername=@shared.ldap.admin.dn@ -ldapAdminPassword=@shared.ldap.admin.password@ -ldapGroupSearchFilter=@shared.ldap.groupSearchFilter@ -authoritiesBaseDN=@shared.ldap.groupSearchBaseDN@ -groupRoleAttribute=@shared.ldap.groupRoleAttribute@ -ldapPendingGroupName=@shared.ldap.ldapPendingGroupName@ diff --git a/config/defaults/catalogapp/WEB-INF/classes/log4j.properties b/config/defaults/catalogapp/WEB-INF/classes/log4j.properties deleted file mode 100644 index d3d11d91d0..0000000000 --- a/config/defaults/catalogapp/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,11 +0,0 @@ -log4j.rootLogger=@shared.default.log.level@, R - -log4j.logger.catalogapp=@shared.default.log.level@, R - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = @shared.log.dir@/catalogapp.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = @shared.log.dir@/catalogapp.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n \ No newline at end of file diff --git a/config/defaults/catalogapp/WEB-INF/jsp/header.jsp b/config/defaults/catalogapp/WEB-INF/jsp/header.jsp deleted file mode 100644 index 89da995d66..0000000000 --- a/config/defaults/catalogapp/WEB-INF/jsp/header.jsp +++ /dev/null @@ -1,28 +0,0 @@ -<%@ page pageEncoding="UTF-8"%> -<%@ page language="java" %> -<%@ page import="java.util.*" %> -<%@ page import="org.springframework.web.context.support.WebApplicationContextUtils" %> -<%@ page import="org.springframework.context.ApplicationContext" %> -<%@ page import="org.springframework.web.servlet.support.RequestContextUtils" %> -<%@ page import="org.georchestra.commons.configuration.GeorchestraConfiguration" %> -<%@ page contentType="text/html; charset=UTF-8"%> - -<% - -String headerHeight = "@shared.header.height@"; -try { - ApplicationContext ctx = RequestContextUtils.getWebApplicationContext(request); - headerHeight = ctx.getBean(GeorchestraConfiguration.class).getProperty("headerHeight"); -} catch (Exception e) {} - -%> - - - - -
- - -
-
-
diff --git a/config/defaults/catalogapp/maven.filter b/config/defaults/catalogapp/maven.filter deleted file mode 100644 index 2d87da9a06..0000000000 --- a/config/defaults/catalogapp/maven.filter +++ /dev/null @@ -1,5 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - -language=@shared.language@ -instance=@shared.instance.name@ diff --git a/config/defaults/downloadform/WEB-INF/classes/log4j.properties b/config/defaults/downloadform/WEB-INF/classes/log4j.properties deleted file mode 100644 index 5abb70be5d..0000000000 --- a/config/defaults/downloadform/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,11 +0,0 @@ -log4j.rootLogger=@shared.default.log.level@, R - -log4j.logger.downloadform=@shared.default.log.level@, R - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = @shared.log.dir@/downloadform.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = @shared.log.dir@/downloadform.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n diff --git a/config/defaults/downloadform/maven.filter b/config/defaults/downloadform/maven.filter deleted file mode 100644 index 5d10bc7635..0000000000 --- a/config/defaults/downloadform/maven.filter +++ /dev/null @@ -1,5 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - -activated=@shared.download_form.activated@ -dlJdbcUrl=@shared.download_form.jdbcurl@?user=@shared.psql.user@&password=@shared.psql.pass@ diff --git a/config/defaults/extractorapp/WEB-INF/classes/log4j.properties b/config/defaults/extractorapp/WEB-INF/classes/log4j.properties deleted file mode 100644 index f11791ac12..0000000000 --- a/config/defaults/extractorapp/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,28 +0,0 @@ -#------------------------------------------------------------------------------ -# -# The following properties set the logging levels and log appender. The -# log4j.rootLogger variable defines the default log level and one or more -# appenders. For the console, use 'S'. For the daily rolling file, use 'R'. -# For an HTML formatted log, use 'H'. -# -# To override the default (rootLogger) log level, define a property of the -# form (see below for available values): -# -# log4j.logger. = -# -# Possible Log Levels: -# FATAL, ERROR, WARN, INFO, DEBUG -# -#------------------------------------------------------------------------------ -log4j.rootLogger=@shared.default.log.level@, R - -log4j.logger.org.georchestra.extractorapp=@shared.default.log.level@, R -log4j.logger.org.geotools=@other.framework.log.level@, R - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = @shared.log.dir@/extractorapp.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = @shared.log.dir@/extractorapp.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n diff --git a/config/defaults/extractorapp/WEB-INF/jsp/header.jsp b/config/defaults/extractorapp/WEB-INF/jsp/header.jsp deleted file mode 100644 index b0d0059c7d..0000000000 --- a/config/defaults/extractorapp/WEB-INF/jsp/header.jsp +++ /dev/null @@ -1,11 +0,0 @@ -<%@ page pageEncoding="UTF-8"%> - - - - -
- - -
-
-
diff --git a/config/defaults/extractorapp/WEB-INF/templates/extractor-email-ack-template.txt b/config/defaults/extractorapp/WEB-INF/templates/extractor-email-ack-template.txt deleted file mode 100644 index 2dc3b392c7..0000000000 --- a/config/defaults/extractorapp/WEB-INF/templates/extractor-email-ack-template.txt +++ /dev/null @@ -1,8 +0,0 @@ -Your extraction request has been registered and will be processed soon. - -If you have not requested an extraction, please just ignore this message. - -This e-mail is sent automatically, any answer will be ignored. - ---- -Sent by @shared.instance.name@ (@shared.homepage.url@) diff --git a/config/defaults/extractorapp/WEB-INF/templates/extractor-email-template.txt b/config/defaults/extractorapp/WEB-INF/templates/extractor-email-template.txt deleted file mode 100644 index 15dab15993..0000000000 --- a/config/defaults/extractorapp/WEB-INF/templates/extractor-email-template.txt +++ /dev/null @@ -1,16 +0,0 @@ -Your extraction has completed with the following status: {global_status} - -By layer: - -{successes} -{failures} -{oversized} - -Your extraction can be downloaded during {expiry} days at the following address: - -{link} - -For more information, please contact your SDI administrator - ---- -Sent by @shared.instance.name@ (@shared.homepage.url@) diff --git a/config/defaults/extractorapp/maven.filter b/config/defaults/extractorapp/maven.filter deleted file mode 100644 index c21b26d6cd..0000000000 --- a/config/defaults/extractorapp/maven.filter +++ /dev/null @@ -1,35 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - -host=@shared.server.name@ -instance=@shared.instance.name@ -language=@shared.language@ - -# The base url of the extractorapp as accessible from outside the intranet -servletUrl=@shared.url.scheme@://@shared.server.name@:@shared.server.port@/extractorapp - -# Hostname of the geoserver that is secured for geOrchestra. -secureHost=@shared.server.name@ - -# Email properties -smtpHost=@shared.smtp.host@ -smtpPort=@shared.smtp.port@ -replyTo=@shared.email.replyTo@ -from=@shared.email.from@ -emailHtml=@shared.email.html@ - -privileged_admin_name=@shared.privileged.geoserver.user@ -privileged_admin_pass=@shared.privileged.geoserver.pass@ - -dlformjdbcurl=@shared.download_form.jdbcurl@?user=@shared.psql.user@&password=@shared.psql.pass@ -dlformactivated=@shared.download_form.activated@ - - -# filled by GenerateConfig.groovy: -maxCoverageExtractionSize=99999999 -maxExtractions=100 -remoteReproject=true -useCommandLineGDAL=false -extractionFolderPrefix='proj-extract-' -emailfactory=org.georchestra.extractorapp.ws.EmailFactoryDefault -emailsubject=[geOrchestra] Your extraction request diff --git a/config/defaults/geofence-webapp/WEB-INF/classes/geofence-datasource-ovr.properties b/config/defaults/geofence-webapp/WEB-INF/classes/geofence-datasource-ovr.properties deleted file mode 100644 index b1768605c4..0000000000 --- a/config/defaults/geofence-webapp/WEB-INF/classes/geofence-datasource-ovr.properties +++ /dev/null @@ -1,57 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - - -################################################################################ -## GeoFence property override file -## -################################################################################ -## Please note that the property keys in this ovr file *do not* have the same -## meaning of the keys in the geofence-datasource.properties file. -## If you need more info about this, please check the doc about -## Spring's PropertyOverrideConfigurer and PropertyPlaceholderConfigurer. -################################################################################ - -################################################################################ -## Override DB connection parameters - -################################################################################ - -geofenceVendorAdapter.databasePlatform=org.hibernatespatial.postgis.PostgisDialect -geofenceDataSource.driverClassName=@shared.psql.jdbc.driver@ -geofenceDataSource.url=jdbc:postgresql://@shared.psql.host@:@shared.psql.port@/@shared.geofence.db@ -geofenceDataSource.username=@shared.psql.user@ -geofenceDataSource.password=@shared.psql.pass@ -geofenceEntityManagerFactory.jpaPropertyMap[hibernate.default_schema]=geofence - -################################################################################ -## Other setup entries -################################################################################ -## hbm2ddl.auto may assume one of these values: -## - validate: validates the DB schema at startup against the internal model. May fail on oracle spatial. -## - update: updates the schema, according to the internal model. Updating automatically the production DB is dangerous. -## - create-drop: drop the existing schema and recreates it according to the internal model. REALLY DANGEROUS, YOU WILL LOSE YOUR DATA. -## You may want not to redefine the property entirely, in order to leave the defult value (no action). - -geofenceEntityManagerFactory.jpaPropertyMap[hibernate.hbm2ddl.auto]=update -geofenceEntityManagerFactory.jpaPropertyMap[javax.persistence.validation.mode]=none -geofenceEntityManagerFactory.jpaPropertyMap[hibernate.validator.apply_to_ddl]=false -geofenceEntityManagerFactory.jpaPropertyMap[hibernate.validator.autoregister_listeners]=false - -################################################################################ -## Configure map base layer - -# filled by GenerateConfig.groovy -geofenceGlobalConfiguration.baseLayerURL= -geofenceGlobalConfiguration.baseLayerName= -geofenceGlobalConfiguration.baseLayerTitle= -geofenceGlobalConfiguration.baseLayerFormat= -geofenceGlobalConfiguration.baseLayerStyle= -geofenceGlobalConfiguration.mapCenterLon= -geofenceGlobalConfiguration.mapCenterLat= -geofenceGlobalConfiguration.mapZoom= -geofenceGlobalConfiguration.mapMaxResolution= -geofenceGlobalConfiguration.mapMaxExtent= -geofenceGlobalConfiguration.mapProjection= - - diff --git a/config/defaults/geofence-webapp/WEB-INF/classes/geofence-ldap.properties b/config/defaults/geofence-webapp/WEB-INF/classes/geofence-ldap.properties deleted file mode 100644 index 33fae4e368..0000000000 --- a/config/defaults/geofence-webapp/WEB-INF/classes/geofence-ldap.properties +++ /dev/null @@ -1,19 +0,0 @@ -############################################################################### -## The geofence properties ldap connection properties. -## -## If geofence is enabled then these values will be used -## otherwise this file is ignored. -############################################################################### -geofenceLdapSource.url=@shared.ldapUrl@ -geofenceLdapSource.base=@shared.ldap.baseDn@ -geofenceLdapSource.user=@shared.ldap.admin.dn@ -geofenceLdapSource.password=@shared.ldap.admin.password@ - -geofenceLdapUserAttributesMapper.id=employeeNumber -geofenceLdapUserAttributesMapper.geometry=l -geofenceLdapUserGroupAttributesMapper.id=ou -geofenceLdapUserGroupAttributesMapper.member=member -gsGroupDAO.searchFilter=objectClass=groupOfMembers -gsUserDAO.searchBase=@shared.ldap.userSearchBaseDN@ -gsUserDAO.userDn=@shared.ldap.uid@=%s,@shared.ldap.userSearchBaseDN@ -gsUserDAO.groupMemberValue=@shared.ldap.uid@=%s,@shared.ldap.userSearchBaseDN@,@shared.ldap.baseDn@ diff --git a/config/defaults/geofence-webapp/WEB-INF/classes/log4j.properties b/config/defaults/geofence-webapp/WEB-INF/classes/log4j.properties deleted file mode 100644 index d062ad5a7a..0000000000 --- a/config/defaults/geofence-webapp/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,23 +0,0 @@ -# Root logger option -log4j.rootLogger=@shared.default.log.level@, file - -# Direct log messages to a log file -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=@shared.log.dir@/geofence.log -log4j.appender.file.MaxFileSize=10MB -log4j.appender.file.MaxBackupIndex=1 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -# Log everything. Good for troubleshooting -#log4j.logger.org.hibernate=DEBUG -#log4j.logger.com.trg=DEBUG - -# Log all JDBC parameters -#log4j.logger.org.hibernate.type=ALL diff --git a/config/defaults/geofence-webapp/WEB-INF/classes/log4j.xml b/config/defaults/geofence-webapp/WEB-INF/classes/log4j.xml deleted file mode 100644 index 5023756144..0000000000 --- a/config/defaults/geofence-webapp/WEB-INF/classes/log4j.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - @shared.log.dir@ - - - - - - - - - - %d{ABSOLUTE} %5p %c{1}:%L - %m%n - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/defaults/geonetwork-main/maven.filter b/config/defaults/geonetwork-main/maven.filter deleted file mode 100644 index 91c5527275..0000000000 --- a/config/defaults/geonetwork-main/maven.filter +++ /dev/null @@ -1,4 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - -# nothing for now \ No newline at end of file diff --git a/config/defaults/geonetwork-main/webapp/WEB-INF/classes/log4j.properties b/config/defaults/geonetwork-main/webapp/WEB-INF/classes/log4j.properties deleted file mode 100644 index b68969c6c6..0000000000 --- a/config/defaults/geonetwork-main/webapp/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,77 +0,0 @@ -# Default Logging Configuration -log4j.rootLogger = OFF - -### GEONETWORK SETTINGS ######################################################## - -log4j.logger.geonetwork = @shared.default.log.level@, jeeves -log4j.logger.geonetwork.search = @shared.default.log.level@ -log4j.logger.geonetwork.spatial = @shared.default.log.level@ -log4j.logger.geonetwork.settings = @shared.default.log.level@ -log4j.logger.geonetwork.schemamanager = @shared.default.log.level@ -log4j.logger.geonetwork.thesaurus-man = @shared.default.log.level@ -log4j.logger.geonetwork.thesaurus = @shared.default.log.level@ -log4j.logger.geonetwork.editorexpandelement = @shared.default.log.level@ -log4j.logger.geonetwork.editoraddelement = @shared.default.log.level@ -log4j.logger.geonetwork.LanguageDetector = @shared.default.log.level@ -log4j.logger.geonetwork.index = @shared.default.log.level@ -log4j.logger.geonetwork.csw = @shared.default.log.level@ -log4j.logger.geonetwork.csw.search = @shared.default.log.level@ -log4j.logger.geonetwork.harvester = @shared.default.log.level@ -log4j.logger.geonetwork.lucene = @shared.default.log.level@ -log4j.logger.geonetwork.ldap = @shared.default.log.level@ -log4j.logger.geonetwork.lucene.tracking = @shared.default.log.level@ -log4j.logger.geonetwork.mef = @shared.default.log.level@ -log4j.logger.geonetwork.z3950server = @shared.default.log.level@ -log4j.logger.geonetwork.z3950 = @shared.default.log.level@ -log4j.logger.geonetwork.sru = @shared.default.log.level@ -log4j.logger.geonetwork.sru.search = @shared.default.log.level@ -log4j.logger.geonetwork.GeoServerPublisher = @shared.default.log.level@ -log4j.logger.geonetwork.data.directory = @shared.default.log.level@ - -log4j.logger.org.springframework = @shared.default.log.level@, jeeves -log4j.logger.org.springframework.* = @shared.default.log.level@ -log4j.logger.org.springframework.security = @shared.default.log.level@, jeeves -log4j.logger.org.springframework.security.ldap = @shared.default.log.level@ - -### JEEVES SETTINGS ############################################################ - -log4j.logger.jeeves = @shared.default.log.level@, jeeves -log4j.logger.jeeves.dbms = @shared.default.log.level@ - -# If resourcetracking is set to DEBUG then each time a resource -# is obtained an exception will be created to track the stack trace -# of where the resource was obtained. The traces can be viewed by -# calling debug.openconnection.accessors as administrator -log4j.logger.jeeves.dbms.resourcetracking = @shared.default.log.level@ - -log4j.logger.jeeves.monitor = @shared.default.log.level@ -log4j.logger.jeeves.engine = @shared.default.log.level@ -log4j.logger.jeeves.dbmspool = @shared.default.log.level@ -log4j.logger.jeeves.resources = @shared.default.log.level@ -log4j.logger.jeeves.xlinkprocessor = @shared.default.log.level@ -log4j.logger.jeeves.transformerFactory = @shared.default.log.level@ -log4j.logger.jeeves.xmlresolver = ERROR - -### JZKIT SETTINGS #### - -log4j.logger.com.k_int=@shared.default.log.level@, jeeves -log4j.logger.org.jzkit=@shared.default.log.level@, jeeves -log4j.logger.org.jzkit.a2j=@shared.default.log.level@, jeeves -log4j.logger.org.jzkit.search.impl.LRUCache = @shared.default.log.level@, console,jeeves - - -### JEEVES APPENDER ############################################################ - -log4j.appender.jeeves = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.jeeves.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.jeeves.RollingPolicy.FileNamePattern = @shared.log.dir@/geonetwork.%d.log.gz -log4j.appender.jeeves.RollingPolicy.ActiveFileName = @shared.log.dir@/geonetwork.log -log4j.appender.jeeves.Append = true -log4j.appender.jeeves.layout = org.apache.log4j.PatternLayout -log4j.appender.jeeves.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n - -### CONSOLE SETTINGS ########################################################### - -log4j.appender.console = org.apache.log4j.ConsoleAppender -log4j.appender.console.layout = org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern=%d{ISO8601} %-5p [%c] - %m%n diff --git a/config/defaults/geonetwork-main/webapp/WEB-INF/config-overrides-georchestra.xml b/config/defaults/geonetwork-main/webapp/WEB-INF/config-overrides-georchestra.xml deleted file mode 100644 index 59b44a35ea..0000000000 --- a/config/defaults/geonetwork-main/webapp/WEB-INF/config-overrides-georchestra.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - geonetwork - @shared.psql.pass@ - @shared.psql.host@ - @shared.psql.port@ - @shared.geonetwork.db@ - 50 - 20 - 0 - @shared.psql.jdbc.driver@ - @shared.psql.jdbc.driver.url@ - @shared.server.name@ - 80 - @shared.server.name@ - 80 - - - @shared.geonetwork.language@ - - - - - - ${postgis.db} - ${postgis.user} - ${postgis.pass} - ${postgis.host} - ${postgis.port} - - - - - main-db - jeeves.resources.dbms.ApacheDBCPool - - ${postgis.user} - ${postgis.pass} - ${pg.jdbc.driver} - jdbc:${pg.jdbc.driver.url}://${postgis.host}:${postgis.port}/${postgis.db} - ${postgis.max.active} - ${postgis.max.idle} - ${postgis.min.idle} - SELECT 1 - - - - download-form-db - jeeves.resources.dbms.ApacheDBCPool - - ${postgis.user} - ${postgis.pass} - ${pg.jdbc.driver} - jdbc:${pg.jdbc.driver.url}://${postgis.host}:${postgis.port}/${postgis.db} - 2 - 1 - 0 - SELECT 1 - - - - - - - - - - - - @shared.url.scheme@://${wfs.host}:${wfs.port}/geoserver/ - - - - - - - - - $1${host}$2 - - $1${port}$2 - - diff --git a/config/defaults/geonetwork-main/webapp/WEB-INF/config-overrides.xml b/config/defaults/geonetwork-main/webapp/WEB-INF/config-overrides.xml deleted file mode 100644 index 9b21d30eac..0000000000 --- a/config/defaults/geonetwork-main/webapp/WEB-INF/config-overrides.xml +++ /dev/null @@ -1,3 +0,0 @@ - - /WEB-INF/config-overrides-georchestra.xml - diff --git a/config/defaults/geonetwork-main/webapp/WEB-INF/config-security-overrides.properties b/config/defaults/geonetwork-main/webapp/WEB-INF/config-security-overrides.properties deleted file mode 100644 index 9bfda3cd20..0000000000 --- a/config/defaults/geonetwork-main/webapp/WEB-INF/config-security-overrides.properties +++ /dev/null @@ -1,28 +0,0 @@ -# Map user information to LDAP attributes and default values -ldapUserContextMapper.mapping[name]=sn, -ldapUserContextMapper.mapping[surname]=givenName, -ldapUserContextMapper.mapping[mail]=mail,data@myorganization.org -ldapUserContextMapper.mapping[organisation]=o,myorganization -ldapUserContextMapper.mapping[kind]=title, -ldapUserContextMapper.mapping[address]=, -ldapUserContextMapper.mapping[zip]=, -ldapUserContextMapper.mapping[state]=, -ldapUserContextMapper.mapping[city]=, -ldapUserContextMapper.mapping[country]=, -ldapUserContextMapper.mapping[privilege]=, -ldapUserContextMapper.mapping[phone]=telephoneNumber, -# If not set, the default profile is RegisteredUser -# Valid profiles are http://geonetwork-opensource.org/manuals/trunk/eng/developer/apidocs/geonetwork/org/fao/geonet/constants/Geonet.Profile.html -ldapUserContextMapper.mapping[profile]=,RegisteredUser - -# Map LDAP custom profiles to catalog profiles. Not used if ldap.privilege.pattern is defined. -#ldapUserContextMapper.profileMapping[Admin]=Administrator -#ldapUserContextMapper.profileMapping[Editeur]=Reviewer -#ldapUserContextMapper.profileMapping[Public]=RegisteredUser - -# geOrchestra LDAP profile mapping -ldapUserContextMapper.profileMapping[ADMIN]=Administrator -ldapUserContextMapper.profileMapping[REVIEWER]=Reviewer -ldapUserContextMapper.profileMapping[EDITOR]=Editor -ldapUserContextMapper.profileMapping[USER]=RegisteredUser -ldapUserContextMapper.profileMapping[ANONYMOUS]=Guest diff --git a/config/defaults/geonetwork-main/webapp/WEB-INF/config-security.properties b/config/defaults/geonetwork-main/webapp/WEB-INF/config-security.properties deleted file mode 100644 index bd0f0ec2d9..0000000000 --- a/config/defaults/geonetwork-main/webapp/WEB-INF/config-security.properties +++ /dev/null @@ -1,69 +0,0 @@ -#Core security properties - -logout.success.url=/index.html -passwordSalt=secret-hash-salt= - -# LDAP Connection Settings -ldap.base.provider.url=@shared.ldapUrl@ -ldap.base.dn=@shared.ldap.baseDn@ -ldap.security.principal=@shared.ldap.admin.dn@ -ldap.security.credentials=@shared.ldap.admin.password@ - -ldap.base.search.base=@shared.ldap.userSearchBaseDN@ -ldap.base.dn.pattern=uid={0} -#ldap.base.dn.pattern=mail={0},${ldap.base.search.base} - -# Define if groups and profile information are imported from LDAP. If not, local database is used. -# When a new user connect first, the default profile is assigned. A user administrator can update -# privilege information. -ldap.privilege.import=true - -ldap.privilege.create.nonexisting.groups=true - - -# Define the way to extract profiles and privileges from the LDAP -# 1. Define one attribute for the profile and one for groups in config-security-overrides.properties - -# 2. Define one attribute for the privilege and define a custom pattern (use LDAPUserDetailsContextMapperWithPattern in config-security.xml). -ldap.privilege.pattern= -#ldap.privilege.pattern=CAT_(.*)_(.*) -ldap.privilege.pattern.idx.group=1 -ldap.privilege.pattern.idx.profil=2 - - -# 3. Define custom location for extracting group and role (no support for group/role combination) (use LDAPUserDetailsContextMapperWithProfileSearch in config-security.xml). -ldap.privilege.search.group.attribute=cn -ldap.privilege.search.group.object=ou=groups -ldap.privilege.search.group.query=(&(objectClass=groupOfMembers)(member=uid={0},${ldap.base.search.base},${ldap.base.dn})(cn=EL_*)) -ldap.privilege.search.group.pattern=EL_(.*) -ldap.privilege.search.privilege.attribute=cn -ldap.privilege.search.privilege.object=ou=groups -ldap.privilege.search.privilege.query=(&(objectClass=groupOfMembers)(member=uid={0},${ldap.base.search.base},${ldap.base.dn})(cn=SV_*)) -ldap.privilege.search.privilege.pattern=SV_(.*) - - - -# Run LDAP sync every day at 23:30 -#ldap.sync.cron=0 30 23 * * ? -ldap.sync.cron=0 * * * * ? -#ldap.sync.cron=0 0/1 * 1/1 * ? * -ldap.sync.startDelay=60000 -ldap.sync.user.search.base=${ldap.base.search.base} -ldap.sync.user.search.filter=(&(objectClass=*)(mail=*@*)(givenName=*)) -ldap.sync.user.search.attribute=uid -ldap.sync.group.search.base=ou=groups -ldap.sync.group.search.filter=(&(objectClass=groupOfMembers)(cn=EL_*)) -ldap.sync.group.search.attribute=cn -ldap.sync.group.search.pattern=EL_(.*) - - -# CAS properties -cas.baseURL=https://@shared.server.name@:443/cas -cas.ticket.validator.url=${cas.baseURL} -cas.login.url=${cas.baseURL}/login -cas.logout.url=${cas.baseURL}/logout?url=${geonetwork.https.url}/ - -# either the hardcoded url to the server -# or if has the form it will be replaced with -# the server details from the server configuration -geonetwork.https.url=https://@shared.server.name@/geonetwork/ diff --git a/config/defaults/geoserver-webapp/WEB-INF/classes/geofence-geoserver.properties b/config/defaults/geoserver-webapp/WEB-INF/classes/geofence-geoserver.properties deleted file mode 100644 index 17cd7b3941..0000000000 --- a/config/defaults/geoserver-webapp/WEB-INF/classes/geofence-geoserver.properties +++ /dev/null @@ -1,28 +0,0 @@ -instanceName=default-gs - -allowRemoteAndInlineLayers=true -allowDynamicStyles=true - -# filled by GenerateConfig.groovy: -servicesUrl= - - -# * monitoring DB -# - must comment out bean -# bean id="hibPropertyConfigurer" class="org.geoserver.config.GeoServerPropertyConfigurer" -# -# on "applicationContext-hib2.xml" into monitoring-.jar - -#database=H2 -#databasePlatform=org.geoserver.monitor.hib.H2Dialect -#showSql=false -#generateDdl=true - -#hibernate.hbm2ddl.auto=update -#hibernate.generate_statistics=true -#hibernate.session_factory_name=SessionFactory -#hibernate.bytecode.use_reflection_optimizer=true -#hibernate.show_sql=false -#hibernate.use_sql_comments=true -#hibernate.format_sql=true -#hibernate.jdbc.use_streams_for_binary=true diff --git a/config/defaults/geoserver-webapp/WEB-INF/classes/org/geoserver/web/GeoServerBasePage.html b/config/defaults/geoserver-webapp/WEB-INF/classes/org/geoserver/web/GeoServerBasePage.html deleted file mode 100644 index eb76c1ddf9..0000000000 --- a/config/defaults/geoserver-webapp/WEB-INF/classes/org/geoserver/web/GeoServerBasePage.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - GeoServer - @shared.instance.name@ - - - - - - - - - - - - - - - -
- - -
- - -
-
- -
- -
- -
-
-
-
- - - diff --git a/config/defaults/geowebcache-webapp/WEB-INF/classes/gwc.properties b/config/defaults/geowebcache-webapp/WEB-INF/classes/gwc.properties deleted file mode 100644 index a0f3eada50..0000000000 --- a/config/defaults/geowebcache-webapp/WEB-INF/classes/gwc.properties +++ /dev/null @@ -1,5 +0,0 @@ -baseUrl=@shared.url.scheme@://@shared.server.name@:@shared.server.port@ -contextPath=/geowebcache - -instance.name=@shared.instance.name@ -header.height=@shared.header.height@ diff --git a/config/defaults/geowebcache-webapp/WEB-INF/classes/log4j.properties b/config/defaults/geowebcache-webapp/WEB-INF/classes/log4j.properties deleted file mode 100644 index 64faf39062..0000000000 --- a/config/defaults/geowebcache-webapp/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,13 +0,0 @@ -# Debugging log settings -log4j.rootLogger=@shared.default.log.level@, R -# ------------- stdout logging --------------------- -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = @shared.log.dir@/geowebcache/geowebcache.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = @shared.log.dir@/geowebcache.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n - -log4j.category.org.geowebcache.seed=@shared.default.log.level@ -log4j.category.org.geowebcache.diskquota=@shared.default.log.level@ diff --git a/config/defaults/header/WEB-INF/classes/log4j.properties b/config/defaults/header/WEB-INF/classes/log4j.properties deleted file mode 100644 index 80eb6771d7..0000000000 --- a/config/defaults/header/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,11 +0,0 @@ -log4j.rootLogger=@shared.default.log.level@, R - -log4j.logger.header=@shared.default.log.level@, R - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = @shared.log.dir@/header.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = @shared.log.dir@/header.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n diff --git a/config/defaults/header/js/.gitignore b/config/defaults/header/js/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/config/defaults/header/maven.filter b/config/defaults/header/maven.filter deleted file mode 100644 index 246fd97273..0000000000 --- a/config/defaults/header/maven.filter +++ /dev/null @@ -1,5 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - -language=@shared.language@ -ldapadminPublicContextPath=@shared.ldapadmin.contextpath@ diff --git a/config/defaults/ldapadmin/WEB-INF/classes/log4j.properties b/config/defaults/ldapadmin/WEB-INF/classes/log4j.properties deleted file mode 100644 index 395c8ab16a..0000000000 --- a/config/defaults/ldapadmin/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,28 +0,0 @@ -#------------------------------------------------------------------------------ -# -# The following properties set the logging levels and log appender. The -# log4j.rootLogger variable defines the default log level and one or more -# appenders. For the console, use 'S'. For the daily rolling file, use 'R'. -# For an HTML formatted log, use 'H'. -# -# To override the default (rootLogger) log level, define a property of the -# form (see below for available values): -# -# log4j.logger. = -# -# Possible Log Levels: -# FATAL, ERROR, WARN, INFO, DEBUG -# -#------------------------------------------------------------------------------ -log4j.rootLogger=@shared.default.log.level@, R - -log4j.logger.org.georchestra.ldapadmin=@shared.default.log.level@, R -log4j.logger.org.georchestra.ldapadmin.ws.utils=INFO, R - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = @shared.log.dir@/ldapadmin.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = @shared.log.dir@/ldapadmin.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n diff --git a/config/defaults/ldapadmin/WEB-INF/templates/account-creation-in-progress-template.txt b/config/defaults/ldapadmin/WEB-INF/templates/account-creation-in-progress-template.txt deleted file mode 100644 index b4120319c6..0000000000 --- a/config/defaults/ldapadmin/WEB-INF/templates/account-creation-in-progress-template.txt +++ /dev/null @@ -1,8 +0,0 @@ -Dear {name}, - -Your request for a new account will be processed very soon. - -Your login is: {uid} - ---- -Sent by @shared.instance.name@ (@shared.homepage.url@) diff --git a/config/defaults/ldapadmin/WEB-INF/templates/account-uid-renamed.txt b/config/defaults/ldapadmin/WEB-INF/templates/account-uid-renamed.txt deleted file mode 100644 index 63af96a3fa..0000000000 --- a/config/defaults/ldapadmin/WEB-INF/templates/account-uid-renamed.txt +++ /dev/null @@ -1,9 +0,0 @@ -Dear {name}, - -This message is intended to let you know that your identifier on the -geOrchestra platform has been modified. - -Your new login is now: {uid} - ---- -Sent by @shared.instance.name@ (@shared.homepage.url@) diff --git a/config/defaults/ldapadmin/WEB-INF/templates/changepassword-email-template.txt b/config/defaults/ldapadmin/WEB-INF/templates/changepassword-email-template.txt deleted file mode 100644 index ff454f5241..0000000000 --- a/config/defaults/ldapadmin/WEB-INF/templates/changepassword-email-template.txt +++ /dev/null @@ -1,12 +0,0 @@ -Dear {name}, - -You (or someone else) asked to reset your password on @shared.homepage.url@. -If you did not request any password update, just ignore this e-mail, you're safe. - -To set a new password for your user ({uid}), go to {url}. -You will then be able to connect to the platform. - -Caution: this e-mail is personal, don't forward it. - ---- -Sent by @shared.instance.name@ (@shared.homepage.url@) diff --git a/config/defaults/ldapadmin/WEB-INF/templates/newaccount-requires-moderation-template.txt b/config/defaults/ldapadmin/WEB-INF/templates/newaccount-requires-moderation-template.txt deleted file mode 100644 index 764b912fb8..0000000000 --- a/config/defaults/ldapadmin/WEB-INF/templates/newaccount-requires-moderation-template.txt +++ /dev/null @@ -1,11 +0,0 @@ -Dear admin, - -A new account has been created on @shared.homepage.url@ and is waiting for validation. - -User name: {name} -User ID: {uid} - -Visit @shared.url.scheme@://@shared.server.name@/ldapadmin/privateui/#/groups/PENDING to review the pending users. - ---- -Sent by @shared.instance.name@ (@shared.homepage.url@) diff --git a/config/defaults/ldapadmin/WEB-INF/templates/newaccount-was-created-template.txt b/config/defaults/ldapadmin/WEB-INF/templates/newaccount-was-created-template.txt deleted file mode 100644 index 389c4b7611..0000000000 --- a/config/defaults/ldapadmin/WEB-INF/templates/newaccount-was-created-template.txt +++ /dev/null @@ -1,10 +0,0 @@ -Dear {name}, - -Your account on "@shared.instance.name@" has been successfully created ! -Visit https://@shared.server.name@/cas/login to login with your identifier "{uid}" and your password. - -Have fun with @shared.instance.name@, - -Your platform administrator ---- -Sent by @shared.instance.name@ (@shared.homepage.url@) diff --git a/config/defaults/ldapadmin/WEB-INF/views/header-privateui.jsp b/config/defaults/ldapadmin/WEB-INF/views/header-privateui.jsp deleted file mode 100644 index 15f6f5812c..0000000000 --- a/config/defaults/ldapadmin/WEB-INF/views/header-privateui.jsp +++ /dev/null @@ -1,36 +0,0 @@ -<%@ page pageEncoding="UTF-8" %> -<%@ page import="org.springframework.web.context.support.WebApplicationContextUtils" %> -<%@ page import="org.springframework.context.ApplicationContext" %> -<%@ page import="org.springframework.web.servlet.support.RequestContextUtils" %> -<%@ page import="org.georchestra.commons.configuration.GeorchestraConfiguration" %> - -<% -String headerHeight = "@shared.header.height@"; -String contextPath = "@shared.ldapadmin.contextpath@"; -try { - ApplicationContext ctx = RequestContextUtils.getWebApplicationContext(request); - if ((ctx.getBean(GeorchestraConfiguration.class) != null) - && (((GeorchestraConfiguration) ctx.getBean(GeorchestraConfiguration.class)).activated())) { - headerHeight = ctx.getBean(GeorchestraConfiguration.class).getProperty("headerHeight"); - contextPath = ctx.getBean(GeorchestraConfiguration.class).getProperty("publicContextPath"); - } -} catch (Exception e) {} - -%> - - - - -
- - -
diff --git a/config/defaults/ldapadmin/WEB-INF/views/header.jsp b/config/defaults/ldapadmin/WEB-INF/views/header.jsp deleted file mode 100644 index a4427e73a2..0000000000 --- a/config/defaults/ldapadmin/WEB-INF/views/header.jsp +++ /dev/null @@ -1,28 +0,0 @@ -<%@ page language="java" pageEncoding="UTF-8" %> -<%@ page import="org.springframework.web.context.support.WebApplicationContextUtils" %> -<%@ page import="org.springframework.context.ApplicationContext" %> -<%@ page import="org.springframework.web.servlet.support.RequestContextUtils" %> -<%@ page import="org.georchestra.commons.configuration.GeorchestraConfiguration" %> - -<% -String headerHeight = "${headerHeight}"; - -try { - ApplicationContext ctx = RequestContextUtils.getWebApplicationContext(request); - if ((ctx.getBean(GeorchestraConfiguration.class) != null) - && (((GeorchestraConfiguration) ctx.getBean(GeorchestraConfiguration.class)).activated())) { - headerHeight = ctx.getBean(GeorchestraConfiguration.class).getProperty("headerHeight"); - } -} catch (Exception e) {} - -%> - - - - -
- - -
-
-
diff --git a/config/defaults/ldapadmin/maven.filter b/config/defaults/ldapadmin/maven.filter deleted file mode 100644 index 41e53b3ca6..0000000000 --- a/config/defaults/ldapadmin/maven.filter +++ /dev/null @@ -1,54 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - -language=@shared.language@ -headerHeight=@shared.header.height@ -publicContextPath=@shared.ldapadmin.contextpath@ -instanceName=@shared.instance.name@ - -# LDAP -ldapUrl=@shared.ldapUrl@ -baseDN=@shared.ldap.baseDn@ -ldapAdminDn=@shared.ldap.admin.dn@ -ldap.admin.password=@shared.ldap.admin.password@ - -accountUniqueNumberField=employeeNumber -groupUniqueNumberField=ou -userSearchBaseDN=@shared.ldap.userSearchBaseDN@ -groupSearchBaseDN=@shared.ldap.groupSearchBaseDN@ - -# list of protected user -# example -#protectedUser.uid1=@shared.privileged.geoserver.user@ -#protectedUser.uid2=other - -protectedUser.uid1=@shared.privileged.geoserver.user@ - -# Database connection -psql.url=@shared.ldapadmin.jdbcurl@ -psql.user=@shared.psql.user@ -psql.pass=@shared.psql.pass@ - -# Email properties -smtpHost=@shared.smtp.host@ -smtpPort=@shared.smtp.port@ - -replyTo=@shared.email.replyTo@ -from=@shared.email.from@ -emailHtml=@shared.email.html@ -warnUserIfUidModified=@shared.ldapadmin.warnifuidmodified@ -# Moderation (enabled by default - we do not want people to be able to gain access to resources without the admin's consent) -moderatorEmail=@shared.administrator.email@ - -# filled by GenerateConfig.groovy -privateKey= -publicKey= -subject.account.created= -subject.account.in.process= -subject.requires.moderation= -subject.change.password= -subject.account.uid.renamed= -moderatedSignup= -delayInDays= -requiredFields= - diff --git a/config/defaults/ldapadmin/privateui/index.html b/config/defaults/ldapadmin/privateui/index.html deleted file mode 100644 index 32b3520e68..0000000000 --- a/config/defaults/ldapadmin/privateui/index.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - LDAPadmin - @shared.instance.name@ - - - - - -
- - -
- -
- -
- {{flash.message}} -
- -
- Boo! - {{flash.message}} -
- -
- -
- -
-
-
- - - - - - - - - - - - - - - diff --git a/config/defaults/ldapadmin/privateui/partials/users-list-table.html b/config/defaults/ldapadmin/privateui/partials/users-list-table.html deleted file mode 100644 index d191a138cc..0000000000 --- a/config/defaults/ldapadmin/privateui/partials/users-list-table.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - User - Organization - - - - - - {{user.sn}} {{user.givenName}} - {{user.o}} - - diff --git a/config/defaults/mapfishapp/WEB-INF/classes/log4j.properties b/config/defaults/mapfishapp/WEB-INF/classes/log4j.properties deleted file mode 100644 index 1130edc279..0000000000 --- a/config/defaults/mapfishapp/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,13 +0,0 @@ -log4j.rootLogger=@shared.default.log.level@, R - -log4j.logger.mapfishapp=@shared.default.log.level@, R -log4j.logger.org.mapfish.print=@shared.default.log.level@, R -log4j.logger.org.geotools=ERROR, R - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = @shared.log.dir@/mapfishapp.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = @shared.log.dir@/mapfishapp.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n diff --git a/config/defaults/mapfishapp/WEB-INF/jsp/header.jsp b/config/defaults/mapfishapp/WEB-INF/jsp/header.jsp deleted file mode 100644 index 30bbdeed6b..0000000000 --- a/config/defaults/mapfishapp/WEB-INF/jsp/header.jsp +++ /dev/null @@ -1,11 +0,0 @@ -<%@ page pageEncoding="UTF-8"%> - - - - -
- - -
-
-
diff --git a/config/defaults/mapfishapp/maven.filter b/config/defaults/mapfishapp/maven.filter deleted file mode 100644 index 2aed76c6db..0000000000 --- a/config/defaults/mapfishapp/maven.filter +++ /dev/null @@ -1,20 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - -instance=@shared.instance.name@ -language=@shared.language@ - - -jdbcUrl=@shared.mapfishapp.jdbcurl@?user=@shared.psql.user@&password=@shared.psql.pass@ - - -#you can provide multiple entries -credentials=\ -\ - \ - \ - \ - - -# filled by GenerateConfig.groovy: -docTempDir= diff --git a/config/defaults/security-proxy/503.jsp b/config/defaults/security-proxy/503.jsp deleted file mode 100644 index 92e88046bf..0000000000 --- a/config/defaults/security-proxy/503.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" - pageEncoding="UTF-8"%> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> - - - - - - - - <s:message code="503.title"/> - - - - - - <%@ include file="header.jsp" %> -
- -

-
- - diff --git a/config/defaults/security-proxy/WEB-INF/classes/log4j.properties b/config/defaults/security-proxy/WEB-INF/classes/log4j.properties deleted file mode 100644 index 4721f589b3..0000000000 --- a/config/defaults/security-proxy/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,42 +0,0 @@ -#------------------------------------------------------------------------------ -# -# The following properties set the logging levels and log appender. The -# log4j.rootLogger variable defines the default log level and one or more -# appenders. For the console, use 'S'. For the daily rolling file, use 'R'. -# For an HTML formatted log, use 'H'. -# -# To override the default (rootLogger) log level, define a property of the -# form (see below for available values): -# -# log4j.logger. = -# -# Possible Log Levels: -# FATAL, ERROR, WARN, INFO, DEBUG -# -#------------------------------------------------------------------------------ -log4j.rootLogger=@shared.default.log.level@, R - -log4j.logger.org.georchestra.security=@shared.default.log.level@ -log4j.logger.org.georchestra.security.statistics=INFO, OGCSTATISTICS -log4j.logger.org.georchestra.security.healthcheck=@other.framework.log.level@ - -log4j.logger.OGCServiceMessageFormatter=DEBUG -log4j.logger.org.springframework=@other.framework.log.level@ -log4j.logger.org.jasig=@other.framework.log.level@ - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = @shared.log.dir@/security-proxy.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = @shared.log.dir@/security-proxy.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n - - -# OGC services statistics -log4j.appender.OGCSTATISTICS=org.georchestra.ogcservstatistics.log4j.OGCServicesAppender -log4j.appender.OGCSTATISTICS.activated=@shared.ogc.statistics.activated@ - -log4j.appender.OGCSTATISTICS.jdbcURL=@shared.ogc.statistics.jdbcurl@ -log4j.appender.OGCSTATISTICS.databaseUser=@shared.psql.user@ -log4j.appender.OGCSTATISTICS.databasePassword=@shared.psql.pass@ diff --git a/config/defaults/security-proxy/header.jsp b/config/defaults/security-proxy/header.jsp deleted file mode 100644 index 2e0b6eca7c..0000000000 --- a/config/defaults/security-proxy/header.jsp +++ /dev/null @@ -1,11 +0,0 @@ -<%@ page pageEncoding="UTF-8" %> - - - - -
- - -
-
-
diff --git a/config/defaults/security-proxy/maven.filter b/config/defaults/security-proxy/maven.filter deleted file mode 100644 index 8a4f3fa370..0000000000 --- a/config/defaults/security-proxy/maven.filter +++ /dev/null @@ -1,39 +0,0 @@ -# It is recommended that you do not copy this file into your own profile -# (unless you know what you're doing) - -public_host=@shared.server.name@ -scheme=@shared.url.scheme@ - -#LDAP config -ldapUrl=@shared.ldapUrl@ -baseDN=@shared.ldap.baseDn@ -userSearchBaseDN=@shared.ldap.userSearchBaseDN@ -authoritiesBaseDN=@shared.ldap.groupSearchBaseDN@ -ldapAdminDn=@shared.ldap.admin.dn@ -ldap.admin.password=@shared.ldap.admin.password@ -authoritiesBaseDN=@shared.ldap.groupSearchBaseDN@ -groupRoleAttribute=@shared.ldap.groupRoleAttribute@ -groupSearchFilter=@shared.ldap.groupSearchFilter@ - -cas.public.host=@shared.server.name@ - -# database health check parameters -psql.host=@shared.psql.host@ -psql.port=@shared.psql.port@ -psql.user=@shared.psql.user@ -psql.pass=@shared.psql.pass@ -checkHealth=@shared.checkhealth.activated@ -psql.db=@shared.psql.checkhealth.db@ -max.database.connections=@shared.checkhealth.max_db_connections@ - -# filled by GenerateConfig.groovy: -cas.private.host= -public.ssl= -private.ssl= -proxy.defaultTarget= -proxy.mapping= -header.mapping= -remove.xforwarded.headers= - -privileged_admin_name=@shared.privileged.geoserver.user@ - diff --git a/config/pom.xml b/config/pom.xml deleted file mode 100644 index 536a8b7a26..0000000000 --- a/config/pom.xml +++ /dev/null @@ -1,337 +0,0 @@ - - - 4.0.0 - - org.georchestra - root - 15.12-SNAPSHOT - - config - jar - Configuration Project - http://www.georchestra.org - - - ${basedir}/shared.maven.filters - ${build.support.dir}/shared.maven.filters - ${generated.output}/shared.maven.filters - - - - ${basedir}/defaults - true - - shared.maven.filters - **/*.class - **/*.jar - **/*.gif - **/*.bmp - **/*.png* - **/*.exe - **/*.jpg - **/*.jpeg - **/*.ico - **/*.odg - **/*.swf - **/*.doc - - - - ${basedir}/defaults - false - - **/*.class - **/*.jar - **/*.gif - **/*.bmp - **/*.png* - **/*.exe - **/*.jpg - **/*.jpeg - **/*.ico - **/*.odg - **/*.swf - **/*.doc - - - - ${conf.base} - true - - excluded/** - build_support/** - **/*.class - **/*.jar - **/*.gif - **/*.bmp - **/*.png* - **/*.exe - **/*.jpg - **/*.jpeg - **/*.ico - **/*.odg - **/*.swf - **/*.doc - - - - ${conf.base} - false - - **/*.class - **/*.jar - **/*.gif - **/*.bmp - **/*.png* - **/*.exe - **/*.jpg - **/*.jpeg - **/*.ico - **/*.odg - **/*.swf - **/*.doc - - - - ${generated.output} - true - - /shared.maven.filters - **/*.class - **/*.jar - **/*.gif - **/*.bmp - **/*.png* - **/*.exe - **/*.jpg - **/*.jpeg - **/*.ico - **/*.odg - **/*.swf - **/*.doc - - - - ${generated.output} - false - - **/*.class - **/*.jar - **/*.gif - **/*.bmp - **/*.png* - **/*.exe - **/*.jpg - **/*.jpeg - **/*.ico - **/*.odg - **/*.swf - **/*.doc - - - - ${project.basedir}/src/main - false - - **/*.groovy - - - - - - org.apache.maven.plugins - maven-resources-plugin - - true - - @ - - - ISO-8859-1 - - - - org.apache.maven.plugins - maven-compiler-plugin - - false - - **/* - - - - - org.codehaus.groovy.maven - gmaven-plugin - - - generateJavaStubsForDocs - generate-sources - - generateStubs - - - - - ${basedir}/src/main/scripts - - **/*.groovy - - - - - - - create-default-gen-config-file - generate-resources - - execute - - - - - - - - generate-config-files - generate-resources - - execute - - - - ${build.support.dir} - ${basedir}/src/main/scripts - - - - - - - tests - test - - execute - - - - ${basedir}/src/test/groovy - ${basedir}/src/main/scripts - - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - ${server} - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9 - - private - true - - - - - javadoc - - compile - - - - - - - - junit - junit - 4.8 - test - - - com.yahoo.platform.yui - yuicompressor - 2.4.7 - - - org.codehaus.groovy - groovy-all - ${groovy-version} - compile - - - - ${basedir}/target/generated - ${basedir}/configurations/${server} - ${conf.base}/build_support/ - println("No post treatment script") - 1.6.0 - - diff --git a/config/shared.maven.filters b/config/shared.maven.filters deleted file mode 100644 index 06b1910f5f..0000000000 --- a/config/shared.maven.filters +++ /dev/null @@ -1,83 +0,0 @@ -# shared values like public url can be put here - -shared.instance.name=geOrchestra - -shared.url.scheme=http -shared.homepage.url=@shared.url.scheme@://@shared.server.name@/ -shared.server.port=80 - -shared.header.height=90 - -shared.language=en -shared.geonetwork.language=eng - -shared.ldap.host=localhost -shared.ldap.port=636 -shared.ldapUrl=ldaps://@shared.ldap.host@:@shared.ldap.port@ -shared.ldap.userSearchBaseDN=ou=users -shared.ldap.uid=uid -shared.ldap.userSearchFilter=@shared.ldap.uid@=%u -shared.ldap.groupSearchBaseDN=ou=groups -shared.ldap.admin.dn=cn=admin,@shared.ldap.baseDn@ -shared.ldap.groupSearchFilter=(member=uid={1},@shared.ldap.userSearchBaseDN@,@shared.ldap.baseDn@) -shared.ldap.ldapPendingGroupName=PENDING -shared.ldap.groupRoleAttribute=cn - -shared.psql.host=localhost -shared.psql.port=5432 -shared.psql.user=www-data -shared.psql.pass=www-data - -# Configure GeoNetwork db connection to use Postgresql or PostGIS -# Enable PostGIS using the following (Install PostGIS first) -#shared.psql.jdbc.driver=org.postgis.DriverWrapper -#shared.psql.jdbc.driver.url=postgresql_postGIS -# Postgresql configuration -shared.psql.jdbc.driver=org.postgresql.Driver -shared.psql.jdbc.driver.url=postgresql - -shared.log.dir=/var/log/tomcat8 -shared.default.log.level=WARN -other.framework.log.level=WARN - -shared.smtp.host=localhost -shared.smtp.port=25 - -shared.email.html=false -shared.email.replyTo=psc\@georchestra.org -shared.email.from=psc\@georchestra.org - -shared.administrator.email=psc\@georchestra.org - -shared.ogc.statistics.activated=false -shared.ogc.statistics.db=georchestra -shared.ogc.statistics.jdbcurl=jdbc:postgresql://@shared.psql.host@:@shared.psql.port@/@shared.ogc.statistics.db@ - -shared.download_form.activated=false -shared.download_form.db=georchestra -shared.download_form.jdbcurl=jdbc:postgresql://@shared.psql.host@:@shared.psql.port@/@shared.download_form.db@ -shared.download_form.pdfurl= - -shared.ldapadmin.contextpath=/ldapadmin -shared.ldapadmin.db=georchestra -shared.ldapadmin.jdbcurl=jdbc:postgresql://@shared.psql.host@:@shared.psql.port@/@shared.ldapadmin.db@ -shared.ldapadmin.warnifuidmodified=true - -shared.mapfishapp.db=georchestra -shared.mapfishapp.jdbcurl=jdbc:postgresql://@shared.psql.host@:@shared.psql.port@/@shared.mapfishapp.db@ - -shared.geonetwork.db=georchestra - -shared.geofence.db=georchestra - - -# -------------------------------------------------- -# HEALTH CHECK properties -# -------------------------------------------------- - -# If the HEALTH CHECK feature is activated, the security proxy -# monitors db connections and reports when a limit is reached. - -shared.checkhealth.activated=false -shared.psql.checkhealth.db=@shared.geonetwork.db@ -shared.checkhealth.max_db_connections=170 diff --git a/config/src/main/scripts/AbstractUpdater.groovy b/config/src/main/scripts/AbstractUpdater.groovy deleted file mode 100644 index eeb8f61767..0000000000 --- a/config/src/main/scripts/AbstractUpdater.groovy +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Abstract class for classes that update an existing file or create a new file - * - *
    - *
  • The 'path' parameter [Optional] a path to add to end of both to and from.
  • - *
  • The 'fromProject' parameter [Optional] if non-null it is the directory name of one of the root projects
  • - *
  • The 'from' parameter [Optional] is a string and is relative to the root of the config project directory
  • - *
  • The 'to' parameter is also a string but is relative to the directory that contains generated files. These files override the files in the normal configuration
  • - *
- * The update method is called with a closure that will update the properties read from the 'from' file and writes the updated properties - * to the 'to' file. - *
    - *
  • Note: if the 'from' parameter is not defined then the properties will be empty when passed to the update method. - *
  • Note: if the 'from' parameter is defined then the file must exist or an error will be thrown - *
  • Note: if any of the parameters have a / it will be replaced with the platform specific path separator -
- */ -abstract class AbstractUpdater { - /** [Optional] a path to add to end of both to and from */ - String path - /** [Optional] if non-null it is the directory name of one of the root projects */ - String from - /** File to write the updated/created file to. This is relative to the directory containing the generated files */ - String to - /** [Optional] if non-null it is the directory name of one of the root projects */ - String fromProject - - protected def getParams() { - return Parameters.get - } - - protected File getFromFile() { - if(from == null) { - return null; - } - def fromFile; - - if(fromProject != null) { - fromFile = new File(params.basedirFile.parent+("/$fromProject/$from").replace('/', File.separator)) - } else { - fromFile = new File(params.basedirFile, from.replace('/', File.separator)) - } - - if (path != null) { - fromFile = new File(fromFile, path.replace('/',File.separator)) - } - - assert fromFile.exists(), "from file: $fromFile does not exist" - - return fromFile; - } - - - protected File getToFile() { - assert to !=null, "the to file cannot be null" - - def toFile = new File(params.outputDir, to.replace('/', File.separator)) - - if (path != null) { - toFile = new File(toFile, path.replace('/',File.separator)) - } - - toFile.parentFile.mkdirs() - - return toFile; - } - -} diff --git a/config/src/main/scripts/FileSet.groovy b/config/src/main/scripts/FileSet.groovy deleted file mode 100644 index ca3c56b9bb..0000000000 --- a/config/src/main/scripts/FileSet.groovy +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Represents a set of files. Can be the files in a directory, a single file or all descendants of a directory. - * If the file set contains several files a sort and a filter can be applied to the files - * - * Note: sorting the files requires loading all the files into memory and sorting. This is both slower and - * requires more memory. - * - * Note: Sorting only applies to a single source. Not to all files in the file set. - - * Examples: - *

- *  // Represents all js files that are descendants of
- *  // $basedirFile/src/main/resources/georchestra/js
- *  // all directories are recursively visited
- *  new FileSet().descendants(
- *    source:"$basedirFile/src/main/resources/georchestra/js", 
- *    filter:{ it.name.endsWith("*.js") }
- *  )
- *  
- *  // Represents a single file
- *  new FileSet().file("App.js")
- *
- *  // Represents the js files directly (not recursively) in the 
- *  // "web-client/src/main/resources/app/search/js" of the geonetwork project
- *  // files are sorted by lastmodified date
- *  new FileSet(project: "geonetwork").children(
- *    source: "web-client/src/main/resources/app/search/js",
- *    filter: {it.name.endsWith("*js")},
- *    sort: {o1, o2 -> o1.lastModified() - o2.lastModified}
- *  )
- *
- *  // A fileset with first App.js then all js files in geonetwork directory
- *  new FileSet().
- *    file("App.js").
- *    children(
- *      source:"geonetwork", 
- *      filter: {it.name.endsWith(".js)}
- *    )
- *  
- * - * The each method can be used to iterate through all the files and perform an action - * on each file in the FileSet - * - */ -class FileSet { - /** - * The project to use as the base of the source paths. If null then the source - * will be interpretted without any modification. - */ - String project - /** NOT API */ - private def sources = [] - - /** - * Iterate through all files in FileSet and pass each to the closure - */ - def each(Closure c) { - sources.each { - it.eachFile(c) - } - } - - /** NOT API */ - private def resolve(def source) { - def s - - if (source instanceof File) { - s = source - } else { - s = new File(source.toString()) - } - def outFile - if(project != null) { - outFile = new File(Parameters.get.basedirFile.parent+("/$project/$s").replace('/', File.separator)) - } else { - outFile = s - } - - if (!outFile.exists()) { - throw new AssertionError("$outFile does not exist. Check definition of FileSet") - } - - return outFile - - } - - /** - * Recursively search for all children of the source. - * - * @param source the root of the file set. Must be a directory - * @param filter - * each file is passed to filter. - * If filter returns true then the file is included. - * If null all descandants are kept. - * @param sort - * Return -1,0,1 (Like comparable) to sort files by any criteria - * If null then no sorting is applied. This is faster and uses less memory - * @return this fileset - */ - FileSet descendants(def source, Closure filter = null, Closure sort = null) { - dir (source, true, filter, sort) - } - - /** - * Search for all children of the source. This is NOT a recursive search - * - * @param source the root of the file set. Must be a directory - * @param filter - * each file is passed to filter. - * If filter returns true then the file is included. - * If null all descandants are kept. - * @param sort - * Return -1,0,1 (Like comparable) to sort files by any criteria - * If null then no sorting is applied. This is faster and uses less memory - * @return this fileset - */ - FileSet children(def source, Closure filter = null, Closure sort = null) { - dir (source, false, filter, sort) - } - - /** NOT API */ - private FileSet dir(def source, Boolean recurse = true, Closure filter = null, Closure sort = null) { - if(filter == null) { - filter = { f -> true } - } - if (recurse == null) { - recurse = true - } - def processDir = { c -> - if (recurse) { - resolve(source).eachFileRecurse { file -> - if (filter(file)) { - c(file) - } - } - } else { - resolve(source).eachFile { file -> - if (filter(file)) { - c(file) - } - } - } - } - sources << [ - eachFile: { c -> - if(sort != null) { - def files = new TreeSet([compare: { o1, o2 -> - return sort(o1,o2) - }] as Comparator) - - processDir {files.add(it)} - - files.each { c(it) } - } else { - processDir (c) - } - } - ] - return this - } - /** - * add a file to the file set - */ - FileSet file (def source) { - sources << [ - eachFile: { c -> - c(resolve(source)) - } - ] - return this - } - - - -} \ No newline at end of file diff --git a/config/src/main/scripts/MavenDownloader.groovy b/config/src/main/scripts/MavenDownloader.groovy deleted file mode 100644 index b049a75a45..0000000000 --- a/config/src/main/scripts/MavenDownloader.groovy +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Downloads a jar from one of the maven repositories to the - * provided location - *

-new MavenDownloader(
-  artifact: ['com.vividsolutions','jts','1.13],
-  to: 'geoserver-webapp/WEB-INF/lib').download()
-
- * Example will download the jts jar and the geoserver control-flow jar to the geoserver configuration - *

-new MavenDownloader(
-  artifacts: [
-    ['org.geoserver.extension','control-flow','2.2.4'],
-    ['com.vividsolutions','jts','1.13]
-  ],
-  to: 'geoserver-webapp/WEB-INF/lib').download()
-
- * Only one of artifact and artifacts can be declared - * the to parameter is always relative to the params.outputDir - */ -class MavenDownloader { - /** The artifacts to download. This is a collections of - * lists(or arrays) that list the artifacts to download. - *

This property is used when one wants to download many artifacts

- *

See artifact complete details on how to define an artifact for download

- *

Note: One and only one of artifacts or artifact is required.

- */ - def artifacts - /** An artifact to download. This is a list (or array) that provides the groupId, artifactId and version - * of the artifact to download. In the future maps will also be permitted so that optional artifact characteristics - * like classifier and type. - *

The format for declaring an artifact is:

- *
{@code
-[groupId,artifactId,version]
-}
- * For example: - *
{@code
-['org.geoserver.extension','control-flow','2.2.4']
-}
- *

Note: One and only one of artifacts or artifact is required.

- */ - def artifact - /** The directory to download the file to. Can be a string or file object */ - def to - - def download () { - if(artifacts != null && artifact != null) { - throw new AssertionError("Only one of artifact or artifacts may be declared") - } - if(artifacts == null && artifact == null) { - throw new AssertionError("One of artifact or artifacts must be declared") - } - if (artifact != null) { - artifacts = [artifact] - } - - artifacts.each {nextArtifact -> - def groupId = nextArtifact[0] - def artifactId = nextArtifact[1] - def version = nextArtifact[2] - def success = false - def params = Parameters.get - - def fileName = "${artifactId}-${version}" - if(nextArtifact.size() == 4) { - fileName += "-${nextArtifact[3]}.jar" - } else { - fileName += ".jar" - } - def path = "${groupId.replace('.','/')}/$artifactId/$version/$fileName" - def toFile = new File(new File(params.outputDir, to), fileName) - if (!toFile.parentFile.exists()) { - if (!toFile.parentFile.mkdirs()) { - throw new AssertionError("Unable to create directory: "+toFile.parentFile+". Check permissions on file and parent directories") - } - } - def errors = "" - for ( repo in params.project.repositories ) { - def url = repo.url - if(url.endsWith("/")) { - url += path - } else { - url += "/"+path - } - try { - if (toFile.exists()) { - if (!toFile.delete()) { - throw new AssertionError("Unable to delete: "+toFile+". Check permissions on file and parent directories") - } - } - - toFile << new URL(url).openStream() - params.log.info("Downloaded $url to "+toFile) - success = true - break; - } catch (e) { - errors != "\n"+repo.url +"->"+ e.message; - } - } - if (!success) { - Log.error("Unable to download artifacts: "+errors) - throw new AssertionError("Failed to download artifact: ${groupId}.${artifactId}.${version}") - } - } - } - -} \ No newline at end of file diff --git a/config/src/main/scripts/Minify.groovy b/config/src/main/scripts/Minify.groovy deleted file mode 100644 index d81c9ef85c..0000000000 --- a/config/src/main/scripts/Minify.groovy +++ /dev/null @@ -1,136 +0,0 @@ -import com.yahoo.platform.yui.compressor.CssCompressor -import com.yahoo.platform.yui.compressor.JavaScriptCompressor - -import org.mozilla.javascript.ErrorReporter; -import org.mozilla.javascript.EvaluatorException; - -import java.io.* - -/** - * Use YUI to minify javascript or css files into a single file. (Obviously css and js files should not be mixed - * Example: - *

- * new Minify(
- *   sources: [
- *     new FileSet().descendants(
- *       source:"$basedirFile/src/main/resources/georchestra/js", 
- *       filter:{ it.name.endsWith("*.js") }
- *     )
- *   ],
- *   output: "$targetDir/classes/apps/georchestra/js/Minified.js")
- * }
- * 
- */ -class Minify { - /** NOT API */ - static final String TAG = "[YUI minification]" - /** - * A list of FileSet objects. There must be at least one source - */ - List sources - /** - * The output file. Can be a string or file object - */ - def output - /** - * if true then we are minifying javascript (default) - * if false then we are minifying css - */ - boolean js = true - /** - * The charset to use for reading the files - */ - String charset = "UTF-8" - /** - * if true then output extra debug information - */ - boolean verbose = false - /** - * munge the variable names. Will result in smaller files - */ - boolean munge = true - /** - * Don't do all the optimizations for the minification. Good for debugging. - */ - boolean disableOptimizations = false - /** - * Keep all semicolons in minified file - */ - boolean preserveAllSemiColons = true - /** - * how wide to allow a line in the minified file - */ - int linebreakpos = 8000 - - /** - * Perform the minification - */ - def execute() { - def params = Parameters.get - def log = params.log - log.info("Beginning minification for $output") - - File outputFile - if(output instanceof File) { - outputFile = output - } else { - def tmp = new File( output.toString().replace("/", File.separator)) - if (tmp.isAbsolute()) { - outputFile = tmp - } else { - outputFile = new File(params.outputDir, tmp.path) - } - } - - outputFile.parentFile.mkdirs() - outputFile.withWriter(charset, { writer -> - sources.each { it.each { file -> - log.info("$TAG compressing: $file") - file.withReader( charset, { reader -> - if (js) { - jsCompress(file, reader, writer, log) - } else { - cssCompress(reader, writer, log) - } - }) - }} - }) - log.info("Done minification for $output") - } - - /** NOT API */ - private formatLog (File file, def message, def sourceName, def line, def lineSource, def lineOffset) { - if (line < 0) { - return " $TAG $file:$message" - } else { - return " $TAG $file:$line:$lineOffset:$message" - } - } - - /** NOT API */ - private void jsCompress(File file, Reader reader, Writer writer, def log) { - def compressor = new JavaScriptCompressor(reader, [ - - warning: { message, sourceName, line, lineSource, lineOffset -> - log.warning(formatLog(file, message, sourceName, line, lineSource, lineOffset)) - }, - - error: { message, sourceName, line, lineSource, lineOffset -> - log.error(formatLog(file, message, sourceName, line, lineSource, lineOffset)) - }, - - runtimeError : { message, sourceName, line, lineSource, lineOffset -> - log.error(formatLog(file, message, sourceName, line, lineSource, lineOffset)) - return new EvaluatorException(message) - } - ] as ErrorReporter) - - compressor.compress(writer, linebreakpos, munge, verbose, preserveAllSemiColons, disableOptimizations) - } - - /** NOT API */ - private void cssCompress(Reader reader, Writer writer, def log) { - CssCompressor compressor = new CssCompressor(reader) - compressor.compress(writer, linebreakpos) - } -} \ No newline at end of file diff --git a/config/src/main/scripts/Parameters.groovy b/config/src/main/scripts/Parameters.groovy deleted file mode 100644 index a47189875b..0000000000 --- a/config/src/main/scripts/Parameters.groovy +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Encapsulates parameters of build. This class is a singleton and - * provides a way to access all the parameters from anywhere in the scripts - * without having to pass all parameters through the entire system. - *

- * To get the Parameter class from anywhere one can do: - *

- * Parameters.get - */ -class Parameters { - /** static reference to the parameters */ - static Parameters get - /** The config/target directory */ - File targetDir - /** If the script is a GenerateConfig script then outputDir is the the 'generated' directory. This is where generated files should go. - Otherwise it is project.build.directory/target - */ - File outputDir - /** The maven base directory of the config module */ - File basedirFile - /** A string indicating the target configuration. This is the value of the server java property and - the name of the configuration directory - */ - String target - /** Optional subtarget normally indicating the server that is being built for. For example - integration, test, prod, int, etc... */ - String subTarget - /** If the script is a GenerateConfig script the directory containing the configuration files. For example: - config/configuration/ - - Otherwise this will be project.build.directory/conf/project.artifactId - */ - File projectDir - /** The directory: config/configuration//build_support if the script is a GenerateConfig script. - Otherwise it is null */ - File buildSupportDir - /** The A SLF4J Logger instance to use for writing logs during the build process
- Example usage: log.info("building")
- */ - def log - /** - * A Groovy ant builder object that can be used to execute many ant tasks: - *

See Using Ant from Groovy

- */ - def ant - /** - * The Maven project object. objects like Repositories and version number and artifact name - * can be accessed through the project - */ - def project - - /** - * Do not call. This is called by framework - */ - def init(def delete) { - get = this - targetDir = new File(project.build.directory) - if (project.artifactId == 'config') { - outputDir = new File(project.build.directory, "generated") - buildSupportDir = new File(project.basedir, "configurations/${target}/build_support") - projectDir = new File(project.basedir, "configurations/${target}") - } else { - outputDir = new File(project.build.directory, "classes") - projectDir = new File(project.build.directory, "conf/"+project.artifactId) - } - basedirFile = project.basedir - - if(delete) { - log.info("cleaning target directory: ${targetDir}") - targetDir.deleteDir() - targetDir.mkdirs() - log.info("cleaning output directory: ${outputDir}") - outputDir.deleteDir() - outputDir.mkdirs() - new File(outputDir, "shared.maven.filters").write("#place holder") - } - } - - String toString() { - return "Params[targetDir=${targetDir}, outputDir=${outputDir}, basedirFile=${basedirFile}, target=${target}, \n\tsubTarget=${subTarget}, projectDir=${projectDir}, buildSupportDir=${buildSupportDir}]" - } -} \ No newline at end of file diff --git a/config/src/main/scripts/PropertyUpdate.groovy b/config/src/main/scripts/PropertyUpdate.groovy deleted file mode 100644 index 1b79848071..0000000000 --- a/config/src/main/scripts/PropertyUpdate.groovy +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Helper class for updating property files. Example usage: - *

-new PropertyUpdate(
-  path: 'maven.filter',
-  from: 'defaults/security-proxy', 
-  to: 'security-proxy').update { properties ->
-    properties['shared.server.name'] = host
-    properties['shared.default.log.level'] = logLevel
-    properties['application_home'] = applicationHome
-    properties['shared.ldapUrl'] = ldapUrl
-}
-
- * Note: source file is not modified. It is read and the updated file - * is written to the directory containing generated files - */ -class PropertyUpdate extends AbstractUpdater { - /** - * Perform the updating. Note: source file is not modified - *

- * Groovy has special syntax for Maps (Properties are map objects so it applies to them) - * See http://groovy.codehaus.org/JN1035-Maps - *

- * Probably the most usefule syntax for our purposes is: - *

    - *
  • properties[key] = newValue
  • - *
- * @param closure that takes a java.util.Properties object and updates the properties. - */ - def update(closure) { - def properties = new Properties() - def fromFile = getFromFile() - if(fromFile != null) { - params.log.info("Loading parameters to update from $fromFile") - fromFile.withReader { r -> - properties.load(r) - } - } - - closure(properties) - - def toFile = getToFile() - params.log.info("Writing updated parameters to $toFile") - params.log.debug("writing "+properties+" to "+toFile) - - toFile.withWriter('UTF-8'){ w -> properties.store(w,"updated by config's GenerateConfig class")} - } -} diff --git a/config/src/main/scripts/TextUpdate.groovy b/config/src/main/scripts/TextUpdate.groovy deleted file mode 100644 index e5223ce642..0000000000 --- a/config/src/main/scripts/TextUpdate.groovy +++ /dev/null @@ -1,44 +0,0 @@ -/** - * - * Updates a text file by replacing all sections of the file that match the patterns - * and outputs it to the configuration
- * Example Usage: - * - *

- * new TextUpdate(
- *   path:  'apps/georchestra/js/Settings.js',
- *   fromProject: "geonetwork",
- *   from: 'web-client/src/main/resources/',
- *   to: 'geonetwork-client/',
- *   patternsToReplace: [ /GeoNetwork\.Util\.defaultLocale\s*=\s*'eng'/: "GeoNetwork.Util.defaultLocale = 'fre'"]
- * ).update()
- * 
- * for a description of path, fromProject, from and to see AbstractUpdater - * - * Note: unlike many other updaters a from file is required - * Note: source file is not modified. It is read and the updated file - * is written to the directory containing generated files - */ -class TextUpdate extends AbstractUpdater { - /** The text replacement definitions */ - def patternsToReplace = [:] - /** - * Load file update text (in-memory) and write results to the generated files directory - */ - def update() { - def fromFile = getFromFile() - def toFile = getToFile() - assert fromFile.exists(), "$fromFile does not exist" - - params.log.info ("updating text file.\n\tfrom: $fromFile\n\tto:$toFile") - - def text = fromFile.text - patternsToReplace.each {entry -> - assert text =~ entry.key, "Unable to find any matches for ${entry.key}" - params.log.info("Performing following replacement: $entry") - text = text.replaceAll(entry.key, entry.value) - } - - toFile << text.getBytes("UTF-8") - } -} diff --git a/config/src/main/scripts/XmlUpdate.groovy b/config/src/main/scripts/XmlUpdate.groovy deleted file mode 100644 index e1ddeb76d0..0000000000 --- a/config/src/main/scripts/XmlUpdate.groovy +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Helper class for updating xml files. Example usage: - * - *

- *  new XmlUpdate(
- *    path: 'file.xml',
- *    from: 'defaults/security-proxy', 
- *    to: 'security-proxy').update { xml ->
- *      xml.category.findAll {it.@class.contains("gn")}. each {cat ->
- *        cat.@class = s.@class + " geor" // add new class to element
- *      }
- *    }
- *
- * Note: source file is not modified. It is read and the updated file - * is written to the directory containing generated files - *

- * The method write passes a MarkupBuilder to the closure: - *

- *

- *

- * The method update passes the xml from XmlParser to the closure: - *

- *

- *

- */ -class XmlUpdate extends AbstractUpdater { - /** - * Create a new xml file with the xml created by the closure - * - * @param closure a closure that takes a MarkupBuilder and uses the builder to create XML - */ - def write(closure) { - def xml, writer - def fromFile = getFromFile() - if(fromFile == null) { - params.log.info("Creating new file") - writer = new StringWriter() - xml = new MarkupBuilder(writer) - } else { - params.log.info("Overwriting $fromFile") - } - closure(doc) - - doUpdate(writer) - } - /** - * Load an existing xml file update the xml in-memory and output updated file to generated directory - * - * @param closure a closure that takes an XmlParser and updates the xml using - the parser. (See link for examples) - */ - def update(closure) { - def xml, writer - def fromFile = getFromFile() - if(fromFile != null) { - params.log.info("Loading parameters to update from $fromFile") - xml = new XmlParser().parse(fromFile) - } else { - throw new AssertionError("$fromFile does not exist. Perhaps you want to use write to create the file") - } - - def writer = new StringWriter() - new XmlNodePrinter(new PrintWriter(writer)).print(xml) - writer(writer) - } - - private def write (writer) { - def text = writer.toString() - def toFile = getToFile() - params.log.info("Writing updated xml to $toFile") - - toFile.withWriter('UTF-8'){ w -> w.write(text)} - } -} diff --git a/config/src/test/groovy/VariableSubstitutionTest.groovy b/config/src/test/groovy/VariableSubstitutionTest.groovy deleted file mode 100644 index 2b1f8bea23..0000000000 --- a/config/src/test/groovy/VariableSubstitutionTest.groovy +++ /dev/null @@ -1,35 +0,0 @@ -/** - * This is ran after the configuration files have been processed and are in the output directory. It - * verifies that all variables have been correctly substituted - */ -public class VariableSubstitutionTest { - def ignorables = [".png", ".gif", ".ico", ".bmp", ".jpg", ".odg", ".pdf", ".swf", ".doc", ".jar", ".class"] - def illegalVariableChars = """!@#{}\$%^&*()+=/|\\"';:,""" - - def execute(def params) { - def failures = [] - params.outputDir.eachFileRecurse{ file -> - def extIndex = file.name.lastIndexOf(".") - def ignorable = file.name.equals("favicon") - if(extIndex > -1) { - def ext = file.name.substring(extIndex).toLowerCase() - ignorable = ignorables.contains(ext) - } - - if(file.isFile() && !ignorable) { - params.log.debug("Testing "+file+" for unsubstituted substitutions") - def m = file.getText("UTF-8") =~ /@\S*@/ - if(m.find()) { - m.each { failure -> - if(!failure.endsWith('\\@') && !failure.findAll{!illegalVariableChars.contains(it)}) { - failures << "${m[0]} was found as an unmatched variable in ${file.path.substring(outputDir.path.length())}. Define the variable in a shared.maven.filters" - } - } - } - } - } - if(!failures.isEmpty()) { - throw new AssertionError(failures.join("\n")) - } - } -} \ No newline at end of file diff --git a/console/.env b/console/.env new file mode 100644 index 0000000000..870f08e540 --- /dev/null +++ b/console/.env @@ -0,0 +1,4 @@ +COMPOSE_PROJECT_NAME=datafeeder_it +SMTP_PORT=2025 +LDAP_PORT=1389 +POSTGRESQL_PORT=54321 diff --git a/console/.gitignore b/console/.gitignore new file mode 100644 index 0000000000..be06a6ad10 --- /dev/null +++ b/console/.gitignore @@ -0,0 +1 @@ +node/ diff --git a/ldapadmin/CLIENT_SERVER_INTERFACE.md b/console/CLIENT_SERVER_INTERFACE.md similarity index 80% rename from ldapadmin/CLIENT_SERVER_INTERFACE.md rename to console/CLIENT_SERVER_INTERFACE.md index 281dab7721..147942f303 100644 --- a/ldapadmin/CLIENT_SERVER_INTERFACE.md +++ b/console/CLIENT_SERVER_INTERFACE.md @@ -145,33 +145,33 @@ Response ### CREATE -Create group +Create role Request - POST groups + POST roles Body parameters { - "cn": "Name of the group", - "description": "Description for the group" + "cn": "Name of the role", + "description": "Description for the role" } Reponse { - "cn": "Name of the group", - "description": "Description for the group" + "cn": "Name of the role", + "description": "Description for the role" } or { "success": false } -### READ (Complete list of groups) +### READ (Complete list of roles) Request - GET groups + GET roles Response @@ -179,8 +179,8 @@ Response "cn": "Administrator", "users": ['pgiraud'] }, { - "cn": "Name of the group", - "description": "Description for the group", + "cn": "Name of the role", + "description": "Description for the role", "users": [] }] @@ -189,29 +189,29 @@ Response Request - GET groups/:cn + GET roles/:cn Response { - "cn": "Name of the group", - "description": "Description for the group", + "cn": "Name of the role", + "description": "Description for the role", "users": ["uid0","uid1"] } ### UPDATE -Modify group +Modify role Request - PUT groups/:cn + PUT roles/:cn Body parameters { - "cn": "New name of the group", - "description": "Modified description for the group" + "cn": "New name of the role", + "description": "Modified description for the role" } Note: *Fields that are not present in the parameters should remain untouched @@ -223,11 +223,11 @@ Reponse ### DELETE -Delete group +Delete role Request - DELETE groups/:cn + DELETE roles/:cn Response @@ -237,11 +237,11 @@ Response ### UPDATE -Add/Remove users to/from the given group +Add/Remove users to/from the given role Request - POST groups_users + POST roles_users Body parameters @@ -261,23 +261,23 @@ Response Curl can be used to test the client-server interface. To get the list of users: - curl --request GET http://georchestra.mydomain.org/ldapadmin/private/users/ + curl --request GET http://georchestra.mydomain.org/console/private/users/ -To create a new group +To create a new role * create a temporary file ``` -nano /tmp/newgroup.json +nano /tmp/newrole.json ``` ``` { - "cn": "Name of the group", - "description": "Description for the group" + "cn": "Name of the role", + "description": "Description for the role" } ``` * post this file - curl -H "Content-Type: application/json" -d @/tmp/newgroup.json --request POST http://georchestra.mydomain.org/ldapadmin/private/groups + curl -H "Content-Type: application/json" -d @/tmp/newrole.json --request POST http://georchestra.mydomain.org/console/private/roles diff --git a/console/README.md b/console/README.md new file mode 100644 index 0000000000..064784baef --- /dev/null +++ b/console/README.md @@ -0,0 +1,174 @@ +# geOrchestra's Console + +![console](https://github.com/georchestra/georchestra/workflows/console/badge.svg) + +The webapp is made of three parts: + * on the public side, it gives the ability to: + * create an account (`/console/account/new`) + * reset your password (`/console/account/passwordRecovery`) + * on the private side known as "the manager" (`/console/manager/`), it allows: + * **administrators** to + * manage users, organisations, roles with a neat user interface + * check platform stats + * **delegated administrators** (on a given set of organisations) to + * create, update, delete users belonging to these organisations + * grant or remove roles (chosen by the administrator) to users belonging to this same set of organisations + * check platform stats + * the user's profile page (`/console/account/userdetails`) allows: + * **referents** to modify their organization details + * **users** to + * modify their details + * view their organisation's detail + * download all the data they created on the platform (as required by the EU General Data Protection Regulation) + * delete their account + +To clarify roles: + * **administrators** are users holding the `SUPERUSER` role. + * **delegated administrators** are special users having the `ORGADMIN` role. +An administrator has set up a delegation for them +(on page `/console/manager/#!/users/${user_id}/delegations`). They can access +the manager, where they can grant a subset of roles to users belonging to a +collection of organisations. + * **referents** are users holding the `REFERENT` role. They are allowed to +modify several of their organisation fields, namely: name, address, logo, +description, website. + * **users** are regular geOrchestra users, obviously having the `USER` role. + + +## Configuration options + +As for every other geOrchestra webapp, its configuration resides in the +(ill-named) [data directory](https://github.com/georchestra/datadir/), +typically something like `/etc/georchestra`, where it expects to find a +`console` sub-directory. + +The most important configuration file is probably +[console.properties](https://github.com/georchestra/datadir/blob/master/console/console.properties), +where the majority of options are commented out, either because there are +default, common values already provided by the datadir's +[default.properties](https://github.com/georchestra/datadir/blob/master/default.properties), +or because the console code offers sensible, built-in, values. + +Below, we go through the most interesting options. + +### Moderated signup + +If the `moderatedSignup` config option is set to `true` (which is the default), +each time a new user requests an account: + * an email is sent to all users having the SUPERUSER role and also to those +which hold an admin delegation for the declared organisation (if any) + * the newly created account is stored inside the "ou=pendingusers" LDAP +organizational unit (which grants zero rights on the SDI). + +If set to `false`, the user is immediately considered as registered, and is +stored inside the "ou=users" LDAP organizational unit. An email is also sent to +all SUPERUSER users and delegated admins if any. + +### Read-Only user identifier + +The `readonlyUid` option, when set to `true`, prevents the user from choosing +its own username on the registration page. It is set to `false` by default. + +If `true`, the username is crafted with the first letter of firstname +concatenated with the user lastname. + +### Required fields + +The `requiredFields` parameter defaults to +`firstName,surname,email,uid,password,confirmPassword`. + +Adding other fields, separated by commas, will make them mandatory in the user +account forms. Note that this parameter does not affect the manager. + +Example: to make the "Organisation" and "Title" fields mandatory, set it to: +``` +requiredFields=firstName,surname,email,uid,password,confirmPassword,org,title +``` +The possible values are: `firstName`, `surname`, `phone`, `facsimile`, `org`, +`title`, `description`, `postalAddress`. **email**, **uid** and **password** +are always required. + +### Organisation types + +Organisations are classified. By default, the console offers the choice between +Association, Company, NGO, Individual, Other. + +This can be set to any other values with: +``` +orgTypeValues=Association,Company,NGO,Individual,Other +``` + +### Custom areas + +Organisations may have an area of competence, built from a collection of +features. + +Features are loaded from a custom endpoint, `/public/area.geojson` which will: + * serve a GeoJSON file from the datadir + * From the filesystem (beware of rights on file) + * from the datadir roots folder + * from the console directory in datadir. + * redirect to a URL if `AreaUrl` starts with `http`. + +The geojson FeatureCollection has to be in EPSG:4326 projection. The native SRS of the layer MUST be in EPSG:4326 + (no on-the-fly transformation). + +Area default url is: +``` +AreasUrl=area.geojson +``` + +Note that it can be set to a custom WFS request like this one: +``` +AreasUrl=https://my.server.org/geoserver/ows?SERVICE=WFS&REQUEST=GetFeature&typeName=gadm:gadm_for_countries&outputFormat=json&cql_filter=ISO='FRA' +``` + +Please pay attention to these options too: `AreasKey`, `AreasValue`, `AreasGroup` +`AreaMapZoom`, `AreaMapCenter`. + +### ReCaptcha + +To fight robots, get a pair of ReCaptcha keys for your site: + * head to [https://www.google.com/recaptcha/admin/create](https://www.google.com/recaptcha/admin/create) + * choose reCAPTCHA version 2 with the "I'm not a robot" checkbox + * fill in your domain + +Once created, set the following parameters: +* `privateKey` +* `publicKey` + + +### Protected Users + +Several user accounts can be protected against deletion or modification, with the `protectedUsersList` property, which holds a comma separated list of user accounts `uid`. These users also do not show up in the manager. + +By default, only `geoserver_privileged_user` (which is internally used by mapfishapp, extractorapp for several operations) is protected: +``` +protectedUsersList=geoserver_privileged_user +``` + +## Developer's corner + +### Integration Testing + +> TL;DR: run integration tests with `mvn verify`. + +While `mvn test` will run the unit tests but not the integration tests, `mvn verify` (or any goal past that, like `mvn install`) is used to run (also) integration tests. + +Integration tests that require an LDAP and/or PostgreSQL database will set them up +using georchestra's "testcontainers". + +To skip integration tests, use the standard `maven-failsafe-plugin`'s flag `-DskipITs`. `maven-surefire-plugin`'s `-DskipTests` +skips both unit and integration tests. + +#### Running from an IDE while developing + +While writing integration tests, it could be cumbersome to wait for the launch and shut down of test containers. + +In this case it's better to have `georchestra/ldap` and `georchestra/database` containers already running and instructing the test run to use them directly. + +To do so launch them externally mapping the ports 389 and 5432 as appropriate, and create a launch configuration for the test class being working on, and set the `-DpgsqlPort= -DldapPort=`. + +When the test case is finished, make sure to run `mvn verify` to check it works properly within the maven build cycle. + +Finally, when writing integration tests, make sure they're self contained and would not be affected by any existing data in the external resources. diff --git a/console/docker-compose.yml b/console/docker-compose.yml new file mode 100644 index 0000000000..4f5f6ee5e2 --- /dev/null +++ b/console/docker-compose.yml @@ -0,0 +1,32 @@ +# Docker composition to run integration tests against. +# Sets up required external services: +# - ldap +# - database +version: '3.8' + +services: + smtp: + image: camptocamp/smtp-sink:latest + #volumes: + # - smtp_maildir:/home/smtp/Maildir/ + ports: + - ${SMTP_PORT}:25 + restart: always + ldap: + image: georchestra/ldap:latest + environment: + - SLAPD_ORGANISATION=georchestra + - SLAPD_DOMAIN=georchestra.org + - SLAPD_PASSWORD=secret + - SLAPD_LOG_LEVEL=32768 # See https://www.openldap.org/doc/admin24/slapdconfig.html#loglevel%20%3Clevel%3E + restart: always + ports: + - ${LDAP_PORT}:389 + database: + image: georchestra/database:latest + environment: + - POSTGRES_USER=georchestra + - POSTGRES_PASSWORD=georchestra + ports: + - ${POSTGRESQL_PORT}:5432 + restart: always diff --git a/ldapadmin/functional-test.odt b/console/functional-test.odt similarity index 100% rename from ldapadmin/functional-test.odt rename to console/functional-test.odt diff --git a/console/pom.xml b/console/pom.xml new file mode 100644 index 0000000000..f2f12d8154 --- /dev/null +++ b/console/pom.xml @@ -0,0 +1,871 @@ + + + + org.georchestra + root + 21.0-SNAPSHOT + + 4.0.0 + console + war + Console + http://www.georchestra.org + + UTF-8 + 1.1.5.RELEASE + ${project.artifactId} + 20210307 + + + + + org.georchestra + georchestra-commons + ${project.version} + + + org.georchestra + georchestra-ldap-account-management + ${project.version} + + + + io.springfox + springfox-swagger2 + 2.9.2 + + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + + org.projectlombok + lombok + + + org.apache.commons + commons-csv + + + junit + junit + test + + + org.hamcrest + hamcrest-core + test + + + org.hamcrest + hamcrest-library + test + + + org.mockito + mockito-all + test + + + org.tuckey + urlrewritefilter + + + commons-logging + commons-logging + + + log4j + log4j + + + javax.servlet + servlet-api + + + + + javax.servlet + jstl + + + log4j + log4j + + + log4j + apache-log4j-extras + jar + runtime + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + + + org.slf4j + slf4j-log4j12 + + + org.aspectj + aspectjrt + + + org.aspectj + aspectjweaver + + + javax.servlet + javax.servlet-api + provided + + + net.sf.flexjson + flexjson + + + org.json + json + + + org.apache.commons + commons-lang3 + + + commons-validator + commons-validator + + + + org.springframework + spring-core + + + org.springframework + spring-test + test + + + org.springframework.security + spring-security-test + test + + + com.jayway.jsonpath + json-path + 2.4.0 + test + + + org.springframework + spring-context + + + org.springframework + spring-aop + + + org.springframework + spring-aspects + + + org.springframework + spring-web + + + org.springframework + spring-webmvc + + + org.springframework + spring-orm + + + org.springframework + spring-jdbc + + + + org.springframework.ldap + spring-ldap-core + + + org.springframework.ldap + spring-ldap-core-tiger + + + org.springframework.ldap + spring-ldap-odm + + + org.springframework.ldap + spring-ldap-ldif-core + + + org.springframework.ldap + spring-ldap-ldif-batch + + + + org.springframework.security + spring-security-core + + + org.springframework.security + spring-security-ldap + + + org.springframework.security + spring-security-config + + + org.springframework.security + spring-security-web + + + + org.springframework.data + spring-data-commons + + + org.springframework.data + spring-data-jpa + + + com.fasterxml.jackson.core + jackson-databind + + + + + commons-digester + commons-digester + + + commons-logging + commons-logging + + + + + commons-io + commons-io + + + commons-fileupload + commons-fileupload + + + javax.el + el-api + provided + + + joda-time + joda-time + + + javax.servlet.jsp + jsp-api + provided + + + commons-codec + commons-codec + + + javax.mail + mail + + + org.apache.tiles + tiles-jsp + + + org.postgresql + postgresql + + + org.hibernate + hibernate-validator-annotation-processor + + + org.hibernate + hibernate-entitymanager + + + com.mchange + c3p0 + + + javax.validation + validation-api + + + + cglib + cglib-nodep + + + javax.transaction + jta + + + com.googlecode.ez-vcard + ez-vcard + + + commons-pool + commons-pool + + + commons-logging + commons-logging + + + + + com.google.guava + guava + + + org.locationtech.jts + jts-core + 1.16.1 + + + org.zeroturnaround + zt-zip + 1.14 + + + org.ldaptive + ldaptive + 1.0.3 + + + com.vladmihalcea + hibernate-types-52 + 2.10.3 + + + + org.powermock + powermock-module-junit4 + test + + + org.powermock + powermock-api-mockito + test + + + org.awaitility + awaitility + test + + + com.github.database-rider + rider-spring + 1.5.1 + test + + + org.slf4j + slf4j-simple + + + + + + net.postgis + postgis-jdbc + 2.5.0 + test + + + org.georchestra + georchestra-testcontainers + test + + + + ${project.artifactId}-${server} + + + net.revelc.code.formatter + formatter-maven-plugin + + + org.apache.maven.plugins + maven-clean-plugin + + + + ${project.basedir}/node + ${project.basedir}/src/main/webapp/manager/public + + + + + + maven-war-plugin + + manager/node_modules/** + + + + org.apache.maven.plugins + maven-resources-plugin + + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + **/*_Roo_* + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + jar-with-dependencies + + + + + org.apache.maven.plugins + maven-deploy-plugin + + + + org.apache.maven.plugins + maven-eclipse-plugin + + + true + false + 2.0 + + + org.eclipse.ajdt.core.ajbuilder + + org.springframework.aspects + + + + org.springframework.ide.eclipse.core.springbuilder + + + + org.eclipse.ajdt.ui.ajnature + com.springsource.sts.roo.core.nature + org.springframework.ide.eclipse.core.springnature + + + + + org.apache.maven.plugins + maven-idea-plugin + + true + true + + + + org.codehaus.mojo + tomcat-maven-plugin + 1.1 + + + org.eclipse.jetty + jetty-maven-plugin + 9.4.27.v20200227 + + + /console + + 5 + + src/main/java/ + src/main/resources/ + src/main/webapp/ + + + 8286 + + + + georchestra.datadir + /etc/georchestra + + + + + + maven-site-plugin + + + attach-descriptor + + attach-descriptor + + + + + + com.github.eirslett + frontend-maven-plugin + 1.6 + + + install-node-and-npm + + install-node-and-npm + + generate-resources + + + npm install + + npm + + generate-resources + + install --unsafe-perm + ${project.basedir} + + + + + ${skip.npm} + v10.18.0 + 6.13.4 + ${project.basedir} + ${project.basedir}/src/main/webapp/manager + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + 2 + false + + + + + + + debianPackage + + ${project.artifactId}-generic + + + org.apache.maven.plugins + maven-scm-plugin + 1.11.2 + + ${project.build.directory}/deb/etc/georchestra + scm:git:https://github.com/georchestra/datadir.git + false + ${packageDatadirScmVersion} + branch + + + + checkout-deb-default-datadir + process-resources + + checkout + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.7 + + + remove-useless-directories + package + + + + + + + + + + + + run + + + + fix-permissions + package + + + + + + + + + + + + + + run + + + + + + net.sf.debian-maven + debian-maven-plugin + 1.0.6 + + georchestra-console + geOrchestra LDAP management + application + ${project.packageVersion} + geOrchestra + PSC + psc@georchestra.org + true + + + + + + + rpmPackage + + ${project.artifactId}-generic + + + org.apache.maven.plugins + maven-scm-plugin + 1.11.2 + + ${project.build.directory}/deb/etc/georchestra + scm:git:https://github.com/georchestra/datadir.git + false + ${packageDatadirScmVersion} + branch + + + + checkout-deb-default-datadir + process-resources + + checkout + + + + + + maven-antrun-plugin + + + + + + + + + + + + + remove-useless-directories + package + + run + + + + + + org.codehaus.mojo + rpm-maven-plugin + 2.1.3 + + + generate-rpm + + rpm + + + + + georchestra-${project.artifactId} + UTF-8 + Applications/Internet + ${rpm.gpg.key} + + + /usr/share/lib/georchestra-${project.artifactId} + + + ${project.build.directory} + + ${project.artifactId}-generic.war + + + + + + / + + + ${project.build.directory}/deb + + + + + + + + + + + docker + + georchestra/${project.artifactId}:${project.version} + scm:git:https://github.com/georchestra/datadir.git + docker-${packageDatadirScmVersion} + + + ${project.artifactId} + + + org.apache.maven.plugins + maven-scm-plugin + 1.11.2 + + ${project.build.directory}/datadir/ + ${dockerDatadirScmUrl} + false + ${dockerDatadirScmVersion} + branch + + + + checkout-docker-default-datadir + process-resources + + checkout + + + + + + com.spotify + docker-maven-plugin + 1.2.2 + + ${dockerImageName} + ${project.basedir}/src/docker + + + /var/lib/jetty/webapps/${context.name} + ${project.build.directory}/${context.name} + + + /etc/georchestra + ${project.build.directory}/datadir + ${project.artifactId}/** + + + docker-hub + https://index.docker.io/v1/ + + + + com.google.guava + guava + 28.2-jre + + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9 + + -Xdoclint:none + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-jxr-plugin + 2.3 + + + org.apache.maven.plugins + maven-pmd-plugin + 2.4 + + 1.6 + + + + org.codehaus.mojo + cobertura-maven-plugin + 2.5.2 + + + maven-project-info-reports-plugin + + false + + 3.0.0 + + + org.apache.maven.plugins + maven-surefire-report-plugin + + + + + geOrchestra + http://www.georchestra.org + + diff --git a/console/src/docker/Dockerfile b/console/src/docker/Dockerfile new file mode 100644 index 0000000000..ad9e87fdf9 --- /dev/null +++ b/console/src/docker/Dockerfile @@ -0,0 +1,20 @@ +FROM jetty:9-jre8 + +ENV XMS=512M XMX=1G + +RUN java -jar "$JETTY_HOME/start.jar" --create-startd --add-to-start=jmx,jmx-remote,stats,http-forwarded + +COPY --chown=jetty:jetty . / + +VOLUME [ "/tmp" ] + +CMD ["sh", "-c", "exec java \ +-Djava.io.tmpdir=/tmp/jetty \ +-Dgeorchestra.datadir=/etc/georchestra \ +-Xms$XMS -Xmx$XMX \ +-XX:-UsePerfData \ +${JAVA_OPTIONS} \ +-Djetty.httpConfig.sendServerVersion=false \ +-Djetty.jmxremote.rmiregistryhost=0.0.0.0 \ +-Djetty.jmxremote.rmiserverhost=0.0.0.0 \ +-jar /usr/local/jetty/start.jar"] diff --git a/console/src/main/java/org/georchestra/console/ConsolePermissionEvaluator.java b/console/src/main/java/org/georchestra/console/ConsolePermissionEvaluator.java new file mode 100644 index 0000000000..f41143f807 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ConsolePermissionEvaluator.java @@ -0,0 +1,77 @@ +package org.georchestra.console; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; + +import org.georchestra.console.dao.AdvancedDelegationDao; +import org.georchestra.console.dao.DelegationDao; +import org.georchestra.console.dto.SimpleAccount; +import org.georchestra.console.model.DelegationEntry; +import org.georchestra.ds.orgs.Org; +import org.georchestra.ds.roles.Role; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.PermissionEvaluator; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +public class ConsolePermissionEvaluator implements PermissionEvaluator { + + private static GrantedAuthority ROLE_SUPERUSER = new SimpleGrantedAuthority("ROLE_SUPERUSER"); + + @Autowired + private DelegationDao delegationDao; + + @Autowired + private AdvancedDelegationDao advancedDelegationDao; + + @Override + public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) { + if (isSuperAdministrator(authentication)) { + return true; + } else { + String username = authentication.getName(); + DelegationEntry delegation = delegationDao.findOne(username); + if (delegation == null) { + return false; + } + + // Filter based on object type + if (targetDomainObject instanceof Role) { + // Filter users in role and role itself + Role r = (Role) targetDomainObject; + List userList = r.getUserList(); + // Remove users not under delegation + userList.retainAll(this.advancedDelegationDao.findUsersUnderDelegation(username)); + r.setFavorite(true); + // Remove role not under delegation + return Arrays.asList(delegation.getRoles()).contains(r.getName()); + } else if (targetDomainObject instanceof Org) { + // Filter org + Org org = (Org) targetDomainObject; + return Arrays.asList(delegation.getOrgs()).contains(org.getId()); + } else if (targetDomainObject instanceof SimpleAccount) { + // filter account + SimpleAccount account = (SimpleAccount) targetDomainObject; + return Arrays.asList(delegation.getOrgs()).contains(account.getOrgId()); + } + } + + return false; + } + + @Override + public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, + Object permission) { + if (isSuperAdministrator(authentication)) { + return true; + } + return false; + } + + private boolean isSuperAdministrator(Authentication authentication) { + return authentication.getAuthorities().contains(ROLE_SUPERUSER); + } + +} diff --git a/console/src/main/java/org/georchestra/console/ReCaptchaV2.java b/console/src/main/java/org/georchestra/console/ReCaptchaV2.java new file mode 100644 index 0000000000..c3b3735e5f --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ReCaptchaV2.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Class used to verify CaptchaV2 response + * + * @author Pierre Jego + * + */ +public class ReCaptchaV2 { + + private static final Log LOG = LogFactory.getLog(ReCaptchaV2.class.getName()); + + /** + * + * @param url + * @param privateKey + * @param gRecaptchaResponse + * + * @return true if validaded on server side by google, false in case of error or + * if an exception occurs + */ + public boolean isValid(String url, String privateKey, String gRecaptchaResponse) { + boolean isValid = false; + + try { + URL obj = new URL(url); + HttpsURLConnection con = (HttpsURLConnection) obj.openConnection(); + + // add request header + con.setRequestMethod("POST"); + String postParams = "secret=" + privateKey + "&response=" + gRecaptchaResponse; + + // Send post request + con.setDoOutput(true); + DataOutputStream wr = new DataOutputStream(con.getOutputStream()); + wr.writeBytes(postParams); + wr.flush(); + wr.close(); + + if (LOG.isDebugEnabled()) { + int responseCode = con.getResponseCode(); + LOG.debug("\nSending 'POST' request to URL : " + url); + LOG.debug("Post parameters : " + postParams); + LOG.debug("Response Code : " + responseCode); + } + + // getResponse + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + // print result + LOG.debug(response.toString()); + + JSONObject captchaResponse; + try { + captchaResponse = new JSONObject(response.toString()); + + if (captchaResponse.getBoolean("success")) { + isValid = true; + } else { + // Error in response + LOG.info("The user response to recaptcha is not valid. The error message is '" + + captchaResponse.getString("error-codes") + + "' - see Error Code Reference at https://developers.google.com/recaptcha/docs/verify."); + } + } catch (JSONException e) { + // Error in response + LOG.error("Error while parsing ReCaptcha JSON response", e); + } + } catch (IOException e) { + LOG.error("An error occured when trying to contact google captchaV2", e); + } + + return isValid; + } + +} diff --git a/console/src/main/java/org/georchestra/console/bs/ExpiredTokenCleanTask.java b/console/src/main/java/org/georchestra/console/bs/ExpiredTokenCleanTask.java new file mode 100644 index 0000000000..d4badec2cc --- /dev/null +++ b/console/src/main/java/org/georchestra/console/bs/ExpiredTokenCleanTask.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.bs; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.console.ds.UserTokenDao; +import org.georchestra.ds.DataServiceException; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * This task searches and removes the expired tokens generated when for the + * "lost password" use case. + * + * @author Mauricio Pazos + * + */ +public class ExpiredTokenCleanTask implements Runnable { + + private static final Log LOG = LogFactory.getLog(ExpiredTokenCleanTask.class.getName()); + + private UserTokenDao userTokenDao; + + private long delayInMilliseconds; + + @Autowired + public ExpiredTokenCleanTask(UserTokenDao userTokenDao) { + + this.userTokenDao = userTokenDao; + } + + public void setDelayInMilliseconds(long delayInMiliseconds) { + + this.delayInMilliseconds = delayInMiliseconds; + } + + /** + * Removes the expired tokens + * + * This task is scheduled taking into account the delay period. + */ + @Override + public void run() { + + Calendar calendar = Calendar.getInstance(); + + long now = calendar.getTimeInMillis(); + Date expired = new Date(now - this.delayInMilliseconds); + + try { + List> userTokenToDelete = userTokenDao.findBeforeDate(expired); + for (Map userToken : userTokenToDelete) { + try { + userTokenDao.delete((String) userToken.get("uid")); + } catch (Exception e) { + LOG.error(e.getMessage()); + } + } + } catch (DataServiceException e1) { + LOG.error(e1); + } + } +} diff --git a/console/src/main/java/org/georchestra/console/bs/ExpiredTokenManagement.java b/console/src/main/java/org/georchestra/console/bs/ExpiredTokenManagement.java new file mode 100644 index 0000000000..4abb45c6fc --- /dev/null +++ b/console/src/main/java/org/georchestra/console/bs/ExpiredTokenManagement.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.bs; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * This class is responsible of checking whether the user user does not use the + * generated token to change his password. + * + *

+ * The period to execute this task is configured. + * + *

+ * + * @author Mauricio Pazos + * + */ +public final class ExpiredTokenManagement { + + private static final Log LOG = LogFactory.getLog(ExpiredTokenManagement.class.getName()); + + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + + /** delay in days to execute the cleaning task */ + private int delayInDays = -1; + + private ExpiredTokenCleanTask expiredTokenCleanTask; + + @Autowired + public ExpiredTokenManagement(ExpiredTokenCleanTask expiredTokenCleanTask) { + this.expiredTokenCleanTask = expiredTokenCleanTask; + } + + public int getDelayInDays() { + return delayInDays; + } + + public void setDelayInDays(int delayInDays) { + this.delayInDays = delayInDays; + } + + public void start() { + + if (delayInDays == -1) { + throw new IllegalStateException("delayInDays property hasn't been set in the configuration bean."); + } + + long delay = toMilliseconds(this.delayInDays); + + // final ExpiredTokenCleanTask expiredTokenCleanTask = new + // ExpiredTokenCleanTask(); + expiredTokenCleanTask.setDelayInMilliseconds(delay); + + this.scheduler.scheduleWithFixedDelay(expiredTokenCleanTask, 0, delay, TimeUnit.MILLISECONDS); + + if (LOG.isDebugEnabled()) { + LOG.debug("was scheduled - delay (days):" + delayInDays); + } + } + + /** + * Converts the days to millisecconds + * + * @param delayInDays + * @return the delay days in milliseconds + */ + private static long toMilliseconds(int delayInDays) { + + return 24 * 3600000 * delayInDays; + } + +} diff --git a/console/src/main/java/org/georchestra/console/bs/ReCaptchaParameters.java b/console/src/main/java/org/georchestra/console/bs/ReCaptchaParameters.java new file mode 100644 index 0000000000..b0efdc4d22 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/bs/ReCaptchaParameters.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.bs; + +import org.springframework.beans.factory.annotation.Required; + +/** + * ReCaptchaParameters attribute + * + * + * @author Sylvain Lesage + * + */ +public final class ReCaptchaParameters { + + private String privateKey; + + private String publicKey; + + private String verifyUrl; + + public String getPublicKey() { + return publicKey; + } + + @Required + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getPrivateKey() { + return privateKey; + } + + @Required + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + public String getVerifyUrl() { + return verifyUrl; + } + + @Required + public void setVerifyUrl(String verifyUrl) { + this.verifyUrl = verifyUrl; + } +} diff --git a/console/src/main/java/org/georchestra/console/dao/AdminLogDao.java b/console/src/main/java/org/georchestra/console/dao/AdminLogDao.java new file mode 100644 index 0000000000..a41b0be12c --- /dev/null +++ b/console/src/main/java/org/georchestra/console/dao/AdminLogDao.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.dao; + +import java.util.Collection; +import java.util.List; + +import org.georchestra.console.model.AdminLogEntry; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +@Repository +public interface AdminLogDao extends PagingAndSortingRepository { + + @Transactional + List findByAdmin(String admin); + + @Transactional + List findByTarget(String target); + + @Transactional + List findByTarget(String target, Pageable range); + + List myFindByTargets(@Param("targets") Collection targets, Pageable range); + +} diff --git a/console/src/main/java/org/georchestra/console/dao/AdvancedDelegationDao.java b/console/src/main/java/org/georchestra/console/dao/AdvancedDelegationDao.java new file mode 100644 index 0000000000..d0478da305 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/dao/AdvancedDelegationDao.java @@ -0,0 +1,83 @@ +package org.georchestra.console.dao; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.sql.DataSource; + +import org.georchestra.console.model.DelegationEntry; +import org.georchestra.ds.orgs.Org; +import org.georchestra.ds.orgs.OrgsDao; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +public class AdvancedDelegationDao { + + public static final GrantedAuthority ROLE_SUPERUSER = new SimpleGrantedAuthority("ROLE_SUPERUSER"); + + @Autowired + private DelegationDao delegationDao; + + @Autowired + private OrgsDao orgsDao; + + @Autowired + private DataSource ds; + + public List findByOrg(String org) throws SQLException { + final String sql = "SELECT uid, array_to_string(orgs, ',') AS orgs, array_to_string(roles, ',') AS roles FROM console.delegations WHERE ? = ANY(orgs)"; + try (Connection c = ds.getConnection(); // + PreparedStatement byOrgStatement = c.prepareStatement(sql)) { + byOrgStatement.setString(1, org); + try (ResultSet resultSet = byOrgStatement.executeQuery()) { + return this.parseResults(resultSet); + } + } + } + + public List findByRole(String cn) throws SQLException { + final String sql = "SELECT uid, array_to_string(orgs, ',') AS orgs, array_to_string(roles, ',') AS roles FROM console.delegations WHERE ? = ANY(roles)"; + try (Connection c = ds.getConnection(); // + PreparedStatement byRoleStatement = c.prepareStatement(sql)) { + byRoleStatement.setString(1, cn); + try (ResultSet resultSet = byRoleStatement.executeQuery()) { + return this.parseResults(resultSet); + } + } + } + + public Set findUsersUnderDelegation(String delegatedAdmin) { + HashSet res = new HashSet(); + + DelegationEntry delegation = this.delegationDao.findOne(delegatedAdmin); + if (delegation == null) { + return res; + } + String[] orgs = delegation.getOrgs(); + for (String o : orgs) { + Org orga = orgsDao.findByCommonName(o); + res.addAll(orga.getMembers()); + } + return res; + } + + private List parseResults(ResultSet sql) throws SQLException { + List res = new LinkedList(); + while (sql.next()) + res.add(this.hydrateFromSQLResult(sql)); + return res; + } + + private DelegationEntry hydrateFromSQLResult(ResultSet sql) throws SQLException { + DelegationEntry res = new DelegationEntry(sql.getString("uid"), sql.getString("orgs").split(","), + sql.getString("roles").split(",")); + return res; + } +} diff --git a/console/src/main/java/org/georchestra/console/dao/AttachmentDao.java b/console/src/main/java/org/georchestra/console/dao/AttachmentDao.java new file mode 100644 index 0000000000..1470235192 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/dao/AttachmentDao.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.dao; + +import org.georchestra.console.model.Attachment; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AttachmentDao extends CrudRepository { + +} diff --git a/console/src/main/java/org/georchestra/console/dao/DelegationDao.java b/console/src/main/java/org/georchestra/console/dao/DelegationDao.java new file mode 100644 index 0000000000..6e17ef4c9b --- /dev/null +++ b/console/src/main/java/org/georchestra/console/dao/DelegationDao.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.dao; + +import org.georchestra.console.model.DelegationEntry; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface DelegationDao extends PagingAndSortingRepository { +} diff --git a/console/src/main/java/org/georchestra/console/dao/EmailDao.java b/console/src/main/java/org/georchestra/console/dao/EmailDao.java new file mode 100644 index 0000000000..2343a2e0a6 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/dao/EmailDao.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.dao; + +import java.util.List; + +import org.georchestra.console.model.EmailEntry; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +@Repository +public interface EmailDao extends CrudRepository { + + @Transactional + List findBySenderOrderByDateDesc(String sender); + + @Transactional + List findByRecipientOrderByDateDesc(String recipient); + +} diff --git a/console/src/main/java/org/georchestra/console/dao/EmailTemplateDao.java b/console/src/main/java/org/georchestra/console/dao/EmailTemplateDao.java new file mode 100644 index 0000000000..bbbd989d89 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/dao/EmailTemplateDao.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.dao; + +import org.georchestra.console.model.EmailTemplate; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface EmailTemplateDao extends CrudRepository { + +} diff --git a/console/src/main/java/org/georchestra/console/ds/AccountGDPRDao.java b/console/src/main/java/org/georchestra/console/ds/AccountGDPRDao.java new file mode 100644 index 0000000000..43ed143906 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ds/AccountGDPRDao.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ds; + +import java.sql.Time; +import java.time.LocalDateTime; +import java.util.List; +import java.util.function.Consumer; + +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.users.Account; +import org.locationtech.jts.geom.Geometry; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; + +/** + */ +public interface AccountGDPRDao { + + static final String DELETED_ACCOUNT_USERNAME = "_deleted_account_"; + + /** + * Deletes all GDPR sensitive records for the given account and returns a + * summary of records affected. Deleting here means making the records + * untraceable to the account owner, but keep them under a "ghost" user name + * ({@code _deleted_account_}) for statistical purposes. + */ + DeletedRecords deleteAccountRecords(@NonNull Account account) throws DataServiceException; + + void visitGeodocsRecords(@NonNull Account owner, @NonNull Consumer consumer); + + void visitOgcStatsRecords(@NonNull Account owner, @NonNull Consumer consumer); + + void visitExtractorRecords(@NonNull Account owner, @NonNull Consumer consumer); + + void visitMetadataRecords(@NonNull Account owner, @NonNull Consumer consumer); + + @Value + class DeletedRecords { + private String accountId; + private int metadataRecords; + private int extractorRecords; + private int geodocsRecords; + private int ogcStatsRecords; + } + + @Value + @Builder + @AllArgsConstructor + class GeodocRecord { + private String standard; + private String rawFileContent; + private String fileHash; + private LocalDateTime createdAt; + private LocalDateTime lastAccess; + private int accessCount; + } + + @Value + @Builder + @AllArgsConstructor + class OgcStatisticsRecord { + private LocalDateTime date; + private String service; + private String layer; + private String request; + private String org; + private List roles; + } + + @Value + @Builder + @AllArgsConstructor + class ExtractorRecord { + private LocalDateTime creationDate; + private Time duration; + private List roles; + private String org; + private String projection; + private Integer resolution; + private String format; + private Geometry bbox; + private String owstype; + private String owsurl; + private String layerName; + private boolean success; + + } + + @Value + @Builder + @AllArgsConstructor + class MetadataRecord { + private long id; + private LocalDateTime createdDate; + private String schemaId; + private String documentContent; + private String userName; + private String userSurname; + } + +} \ No newline at end of file diff --git a/console/src/main/java/org/georchestra/console/ds/AccountGDPRDaoImpl.java b/console/src/main/java/org/georchestra/console/ds/AccountGDPRDaoImpl.java new file mode 100644 index 0000000000..3f7c5f9a95 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ds/AccountGDPRDaoImpl.java @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ds; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.TemporalAccessor; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import javax.annotation.Nullable; +import javax.sql.DataSource; + +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.users.Account; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKBReader; +import org.springframework.beans.factory.annotation.Autowired; + +import com.google.common.annotations.VisibleForTesting; + +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +/** + */ +@Slf4j +public class AccountGDPRDaoImpl implements AccountGDPRDao { + + /** + * DatTimeFormatter to parse timestamps we they come in geonetworks' text column + */ + @VisibleForTesting + public static final DateTimeFormatter GEONETWORK_DATE_FORMAT = new DateTimeFormatterBuilder()// + .parseCaseInsensitive()// + .append(DateTimeFormatter.ISO_LOCAL_DATE)// + .appendLiteral('T')// 'T' instead of space (http://jkorpela.fi/iso8601.html) + .append(DateTimeFormatter.ISO_LOCAL_TIME)// + .toFormatter(); + + private static final String QUERY_METADATA_RECORDS = "select md.id, md.createdate, md.schemaid, md.data, u.name, u.surname" + + " from geonetwork.metadata md left join geonetwork.users u on md.owner = u.id"// + + " where u.username = ?"; + + private static final String DELETE_METADATA_RECORDS = "update geonetwork.users set username = ?, name = ?, surname = ? where username = ?"; + private static final String QUERY_USER_ID_METADATA_RECORDS = "select id from geonetwork.users where username = ?"; + private static final String COUNT_USER_ID_METADATA_RECORDS = "select count(*) from geonetwork.metadata where owner = ?"; + + private static final String QUERY_EXTRACTORAPP_RECORDS = "select log.creation_date, log.duration, log.roles, log.org," + + " layer.projection, layer.resolution, layer.format, ST_AsBinary(layer.bbox) as bbox, layer.owstype, layer.owsurl, layer.layer_name, layer.is_successful" + + " from extractorapp.extractor_log log left join extractorapp.extractor_layer_log layer on log.id = layer.extractor_log_id" + + " where log.username = ?"; + private static final String COUNT_EXTRACTORAPP_RECORDS = "select count(*) " + + " from extractorapp.extractor_log log left join extractorapp.extractor_layer_log layer on log.id = layer.extractor_log_id" + + " where log.username = ?"; + + private static final String DELETE_EXTRACTORAPP_RECORDS = "update extractorapp.extractor_log set username = ? where username = ?"; + + private static final String QUERY_GEODOCS_RECORDS = "select standard, raw_file_content, file_hash, created_at, last_access, access_count" + + " from mapfishapp.geodocs where username = ?"; + private static final String DELETE_GEODOCS_RECORDS = "update mapfishapp.geodocs set username = ? where username = ?"; + + private static final String QUERY_OGCSTATS_RECORDS = "select date, service, layer, id, request, org, roles from ogcstatistics.ogc_services_log where user_name = ?"; + private static final String DELETE_OGCSTATS_RECORDS = "update ogcstatistics.ogc_services_log set user_name = ? where user_name = ?"; + + @Autowired + private DataSource ds; + + public void setDataSource(DataSource dataSource) { + this.ds = dataSource; + } + + /** + * Deletes (obfuscates) all GDPR sensitive records for the given account and + * returns a summary of records affected. Deleting here means making the records + * untraceable to the account owner, but keep them under a "ghost" user name + * ({@code _deleted_account_}) for statistical purposes. + */ + public @Override DeletedRecords deleteAccountRecords(@NonNull Account account) throws DataServiceException { + try (Connection conn = ds.getConnection()) { + conn.setAutoCommit(false); + try { + int metadataRecords; + int extractorRecords; + int geodocsRecords; + int ogcStatsRecords; + metadataRecords = deleteUserMetadataRecords(conn, account); + extractorRecords = deleteUserExtractorRecords(conn, account); + geodocsRecords = deleteUserGeodocsRecords(conn, account); + ogcStatsRecords = deleteUserOgcStatsRecords(conn, account); + conn.commit(); + DeletedRecords ret = new DeletedRecords(account.getUid(), metadataRecords, extractorRecords, + geodocsRecords, ogcStatsRecords); + log.info("Deleted records: {}", ret); + return ret; + } catch (SQLException e) { + conn.rollback(); + throw new DataServiceException(e); + } finally { + conn.setAutoCommit(true); + } + } catch (SQLException e) { + throw new DataServiceException(e); + } + } + + private int deleteUserOgcStatsRecords(Connection conn, @NonNull Account account) throws SQLException { + try (PreparedStatement ps = conn.prepareStatement(DELETE_OGCSTATS_RECORDS)) { + ps.setString(1, DELETED_ACCOUNT_USERNAME); + ps.setString(2, account.getUid()); + return ps.executeUpdate(); + } + } + + private int deleteUserGeodocsRecords(Connection conn, @NonNull Account account) throws SQLException { + try (PreparedStatement ps = conn.prepareStatement(DELETE_GEODOCS_RECORDS)) { + ps.setString(1, DELETED_ACCOUNT_USERNAME); + ps.setString(2, account.getUid()); + return ps.executeUpdate(); + } + } + + private int deleteUserExtractorRecords(Connection conn, @NonNull Account account) throws SQLException { + int count; + try (PreparedStatement ps = conn.prepareStatement(COUNT_EXTRACTORAPP_RECORDS)) { + ps.setString(1, account.getUid()); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + count = rs.getInt(1); + } else { + return 0; + } + } + } + try (PreparedStatement ps = conn.prepareStatement(DELETE_EXTRACTORAPP_RECORDS)) { + ps.setString(1, DELETED_ACCOUNT_USERNAME); + ps.setString(2, account.getUid()); + ps.executeUpdate(); + } + return count; + } + + private int deleteUserMetadataRecords(Connection conn, @NonNull Account account) throws SQLException { + final long userID; + try (PreparedStatement ps = conn.prepareStatement(QUERY_USER_ID_METADATA_RECORDS)) { + ps.setString(1, account.getUid()); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + userID = rs.getLong(1); + } else { + return 0; + } + } + } + int count = 0; + try (PreparedStatement ps = conn.prepareStatement(COUNT_USER_ID_METADATA_RECORDS)) { + ps.setLong(1, userID); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + count = rs.getInt(1); + } else { + return 0; + } + } + } + final String ghostUsername = DELETED_ACCOUNT_USERNAME + userID; + try (PreparedStatement ps = conn.prepareStatement(DELETE_METADATA_RECORDS)) { + ps.setString(1, ghostUsername);// username + ps.setString(2, ghostUsername);// name + ps.setString(3, ghostUsername);// surname + ps.setString(4, account.getUid()); + ps.executeUpdate(); + } + return count; + } + + public @Override void visitGeodocsRecords(@NonNull Account owner, @NonNull Consumer consumer) { + + final String userName = owner.getUid(); + try { + int reccount = visitRecords(QUERY_GEODOCS_RECORDS, ps -> ps.setString(1, userName), + AccountGDPRDaoImpl::createGeodocRecord, consumer); + log.info("Extracted {} geodocs records for user {}", reccount, userName); + } catch (DataServiceException e) { + throw new IllegalStateException(e); + } + } + + public @Override void visitOgcStatsRecords(@NonNull Account owner, + @NonNull Consumer consumer) { + + final String userName = owner.getUid(); + try { + int reccount = visitRecords(QUERY_OGCSTATS_RECORDS, ps -> ps.setString(1, userName), + AccountGDPRDaoImpl::createOgcStatisticsRecord, consumer); + log.info("Extracted {} OGC statistics records for user {}", reccount, userName); + } catch (DataServiceException e) { + throw new IllegalStateException(e); + } + } + + public @Override void visitExtractorRecords(@NonNull Account owner, @NonNull Consumer consumer) { + + final String userName = owner.getUid(); + int reccount; + try { + reccount = visitRecords(QUERY_EXTRACTORAPP_RECORDS, ps -> ps.setString(1, userName), + AccountGDPRDaoImpl::createExtractorRecord, consumer); + log.info("Extracted {} metadata records for user {}", reccount, userName); + } catch (DataServiceException e) { + throw new IllegalStateException(e); + } + } + + public @Override void visitMetadataRecords(@NonNull Account owner, @NonNull Consumer consumer) { + + final String userName = owner.getUid(); + try { + int reccount = visitRecords(QUERY_METADATA_RECORDS, ps -> ps.setString(1, userName), + AccountGDPRDaoImpl::createMetadataRecord, consumer); + log.info("Extracted {} extractorapp records for user {}", reccount, userName); + } catch (DataServiceException e) { + throw new IllegalStateException(e); + } + } + + @FunctionalInterface + interface PreparedStatementBuilder { + void accept(PreparedStatement ps) throws SQLException; + } + + private int visitRecords(String psQuery, PreparedStatementBuilder psBuilder, Function mapper, + @NonNull Consumer consumer) throws DataServiceException { + + try (Connection c = ds.getConnection(); PreparedStatement ps = c.prepareStatement(psQuery)) { + + psBuilder.accept(ps); + + int count = 0; + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + R record = mapper.apply(rs); + consumer.accept(record); + count++; + } + } + return count; + } catch (IllegalStateException e) { + throw new DataServiceException(e.getCause() == null ? e : e.getCause()); + } catch (SQLException e) { + throw new DataServiceException(e); + } + } + + public static MetadataRecord createMetadataRecord(ResultSet rs) { + + MetadataRecord.MetadataRecordBuilder builder = MetadataRecord.builder(); + try { + builder.id(rs.getLong("id")); + // createdate character varying(30) + // e.g. "2019-05-07T00:05:56" + String createDateString = rs.getString("createdate"); + LocalDateTime dateTime = ifNonNull(createDateString, value -> { + TemporalAccessor parsed = GEONETWORK_DATE_FORMAT.parse(value); + return LocalDateTime.from(parsed); + }); + + builder.createdDate(dateTime); + builder.schemaId(rs.getString("schemaid")); + builder.documentContent(rs.getString("data")); + builder.userName(rs.getString("name")); + builder.userSurname(rs.getString("surname")); + return builder.build(); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + } + + public static GeodocRecord createGeodocRecord(ResultSet rs) { + GeodocRecord.GeodocRecordBuilder builder = GeodocRecord.builder(); + try { + builder.standard(rs.getString("standard")); + builder.rawFileContent(rs.getString("raw_file_content")); + builder.fileHash(rs.getString("file_hash")); + builder.createdAt(ifNonNull(rs.getTimestamp("created_at"), Timestamp::toLocalDateTime)); + Timestamp lastAccess = rs.getTimestamp("last_access"); + builder.lastAccess(lastAccess == null ? null : lastAccess.toLocalDateTime()); + builder.accessCount(rs.getInt("access_count")); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + return builder.build(); + } + + public static ExtractorRecord createExtractorRecord(ResultSet rs) { + ExtractorRecord.ExtractorRecordBuilder builder = ExtractorRecord.builder(); + try { + byte[] bytes = rs.getBytes("bbox"); + if (bytes != null && bytes.length > 0) { + WKBReader bboxReader = new WKBReader(); + Geometry bbox = bboxReader.read(bytes); + builder.bbox(bbox); + } + builder.creationDate(ifNonNull(rs.getTimestamp("creation_date"), Timestamp::toLocalDateTime)); + builder.duration(rs.getTime("duration")); + builder.roles(getStringArray(rs, "roles")); + builder.org(rs.getString("org")); + builder.projection(rs.getString("projection")); + int resolution = rs.getInt("resolution"); + if (resolution != 0) {// resolution is nullable + builder.resolution(resolution); + } + builder.format(rs.getString("format")); + builder.owstype(rs.getString("owstype")); + builder.owsurl(rs.getString("owsurl")); + builder.layerName(rs.getString("layer_name")); + builder.success(rs.getBoolean("is_successful")); + } catch (ParseException bboxParseError) { + log.warn("Unable to parse bbox", bboxParseError); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + return builder.build(); + } + + public static OgcStatisticsRecord createOgcStatisticsRecord(ResultSet rs) { + OgcStatisticsRecord.OgcStatisticsRecordBuilder builder = OgcStatisticsRecord.builder(); + try { + builder.date(ifNonNull(rs.getTimestamp("date"), Timestamp::toLocalDateTime)); + builder.service(rs.getString("service")); + builder.layer(rs.getString("layer")); + builder.request(rs.getString("request")); + builder.org(rs.getString("org")); + builder.roles(getStringArray(rs, "roles")); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + return builder.build(); + } + + private static @NonNull List getStringArray(ResultSet rs, String column) throws SQLException { + java.sql.Array array = rs.getArray(column); + if (array != null) { + String[] javaArray = (String[]) array.getArray(); + if (javaArray != null) { + return Arrays.asList(javaArray); + } + } + return Collections.emptyList(); + } + + private static R ifNonNull(@Nullable V value, Function mapper) { + return value == null ? null : mapper.apply(value); + } + +} \ No newline at end of file diff --git a/console/src/main/java/org/georchestra/console/ds/DatabaseSchema.java b/console/src/main/java/org/georchestra/console/ds/DatabaseSchema.java new file mode 100644 index 0000000000..30b4b8aac0 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ds/DatabaseSchema.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ds; + +/** + * Provide information about the database schema + * + * @author Mauricio Pazos + * + */ +interface DatabaseSchema { + + final static String TABLE_USER_TOKEN = "user_token"; + final static String SCHEMA_NAME = "console"; + + // columns + final static String UID_COLUMN = "uid"; + final static String TOKEN_COLUMN = "token"; + final static String CREATION_DATE_COLUMN = "creation_date"; + +} diff --git a/console/src/main/java/org/georchestra/console/ds/DeleteUserTokenCommand.java b/console/src/main/java/org/georchestra/console/ds/DeleteUserTokenCommand.java new file mode 100644 index 0000000000..2750cf2fce --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ds/DeleteUserTokenCommand.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ds; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.georchestra.lib.sqlcommand.AbstractUpdateCommand; + +/** + * Deletes the user_token association. + * + * @author Mauricio Pazos + * + */ +final class DeleteUserTokenCommand extends AbstractUpdateCommand { + + private static final String SQL = "DELETE FROM " + DatabaseSchema.SCHEMA_NAME + "." + + DatabaseSchema.TABLE_USER_TOKEN + " WHERE " + DatabaseSchema.UID_COLUMN + " = ?"; + + private String uid; + + public void setUid(String uid) { + + this.uid = uid; + } + + @Override + protected PreparedStatement prepareStatement(Connection connection) throws SQLException { + + PreparedStatement pStmt = connection.prepareStatement(SQL); + + pStmt.setString(1, this.uid); + + return pStmt; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ds/InsertUserTokenCommand.java b/console/src/main/java/org/georchestra/console/ds/InsertUserTokenCommand.java new file mode 100644 index 0000000000..576424e94d --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ds/InsertUserTokenCommand.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ds; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Map; + +import org.georchestra.lib.sqlcommand.AbstractUpdateCommand; + +/** + * Inserts the token associated to the user in the table "USER_TOKEN". + * + * @author Mauricio Pazos + */ +final class InsertUserTokenCommand extends AbstractUpdateCommand { + + private static final String SQL_INSERT = "INSERT INTO " + DatabaseSchema.SCHEMA_NAME + "." + + DatabaseSchema.TABLE_USER_TOKEN + " (" + DatabaseSchema.UID_COLUMN + "," + DatabaseSchema.TOKEN_COLUMN + + "," + DatabaseSchema.CREATION_DATE_COLUMN + ") VALUES (?, ?, ?)"; + + private Map rowValues; + + /** + * Sets the uid and token in the command. To + * + * @param row (UID_COLUMN, value)(TOKEN_COLUMN, value) (TIMESTAMP_COLUMN, value) + */ + public void setRowValues(final Map row) { + + assert row.keySet().size() == 3; + + this.rowValues = row; + } + + @Override + protected PreparedStatement prepareStatement(Connection connection) throws SQLException { + PreparedStatement pStmt = connection.prepareStatement(SQL_INSERT); + + pStmt.setString(1, (String) this.rowValues.get(DatabaseSchema.UID_COLUMN)); + pStmt.setString(2, (String) this.rowValues.get(DatabaseSchema.TOKEN_COLUMN)); + pStmt.setTimestamp(3, (Timestamp) this.rowValues.get(DatabaseSchema.CREATION_DATE_COLUMN)); + + return pStmt; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ds/QueryByTokenCommand.java b/console/src/main/java/org/georchestra/console/ds/QueryByTokenCommand.java new file mode 100644 index 0000000000..fde0c61f1b --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ds/QueryByTokenCommand.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ds; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +/** + * Searches the user_token association which matches with the provided token. + * + * @author Mauricio Pazos + * + */ +final class QueryByTokenCommand extends org.georchestra.lib.sqlcommand.AbstractQueryCommand { + + private String token; + + public void setToken(String token) { + + this.token = token; + } + + /** + * builds the sql query + * + * @return the sql statement + */ + private String getSQLStatement() { + + StringBuilder sql = new StringBuilder(); + + sql.append(" SELECT ").append(DatabaseSchema.UID_COLUMN).append(",").append(DatabaseSchema.TOKEN_COLUMN) + .append(",").append(DatabaseSchema.CREATION_DATE_COLUMN).append(" FROM ") + .append(DatabaseSchema.SCHEMA_NAME + "." + DatabaseSchema.TABLE_USER_TOKEN) + .append(" WHERE " + DatabaseSchema.TOKEN_COLUMN + " = ?"); + + return sql.toString(); + } + + @Override + protected PreparedStatement prepareStatement(Connection connection) throws SQLException { + PreparedStatement pStmt = connection.prepareStatement(getSQLStatement()); + + pStmt.setString(1, this.token); + + return pStmt; + } + + @Override + protected Map getRow(ResultSet rs) throws SQLException { + + Map row = new HashMap(3); + row.put(DatabaseSchema.UID_COLUMN, rs.getString(DatabaseSchema.UID_COLUMN)); + row.put(DatabaseSchema.TOKEN_COLUMN, rs.getString(DatabaseSchema.TOKEN_COLUMN)); + row.put(DatabaseSchema.CREATION_DATE_COLUMN, rs.getTimestamp(DatabaseSchema.CREATION_DATE_COLUMN)); + + return row; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ds/QueryByUidCommand.java b/console/src/main/java/org/georchestra/console/ds/QueryByUidCommand.java new file mode 100644 index 0000000000..7a0a08eef4 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ds/QueryByUidCommand.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ds; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +/** + * Command to search a row, in the user_token table, by uid. + * + * @author Mauricio Pazos + * + */ +class QueryByUidCommand extends org.georchestra.lib.sqlcommand.AbstractQueryCommand { + + private String uid; + + public void setUid(final String uid) { + this.uid = uid; + } + + /** + * builds the sql query + * + * @return the sql statement + */ + private String getSQLStatement() { + + StringBuilder sql = new StringBuilder(); + + sql.append(" SELECT ").append(DatabaseSchema.UID_COLUMN).append(",").append(DatabaseSchema.TOKEN_COLUMN) + .append(",").append(DatabaseSchema.CREATION_DATE_COLUMN).append(" FROM ") + .append(DatabaseSchema.SCHEMA_NAME + "." + DatabaseSchema.TABLE_USER_TOKEN).append(" WHERE uid = ?"); + + return sql.toString(); + } + + /** + * Prepares the Statement setting the year and month. + */ + @Override + protected PreparedStatement prepareStatement(Connection connection) throws SQLException { + + PreparedStatement pStmt = connection.prepareStatement(getSQLStatement()); + + pStmt.setString(1, this.uid); + + return pStmt; + } + + @Override + protected Map getRow(ResultSet rs) throws SQLException { + + Map row = new HashMap(3); + row.put(DatabaseSchema.UID_COLUMN, rs.getString(DatabaseSchema.UID_COLUMN)); + row.put(DatabaseSchema.TOKEN_COLUMN, rs.getString(DatabaseSchema.TOKEN_COLUMN)); + row.put(DatabaseSchema.CREATION_DATE_COLUMN, rs.getTimestamp(DatabaseSchema.CREATION_DATE_COLUMN)); + + return row; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ds/QueryUserTokenExpiredCommand.java b/console/src/main/java/org/georchestra/console/ds/QueryUserTokenExpiredCommand.java new file mode 100644 index 0000000000..192baab063 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ds/QueryUserTokenExpiredCommand.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ds; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Searches the tokens before date provided. + * + * @author Mauricio Pazos + * + */ +class QueryUserTokenExpiredCommand extends org.georchestra.lib.sqlcommand.AbstractQueryCommand { + + private Date beforeDate; + + public void setBeforeDate(final Date beforeDate) { + this.beforeDate = beforeDate; + } + + /** + * builds the sql query + * + * @return the sql statement + */ + private String getSQLStatement() { + + StringBuilder sql = new StringBuilder(); + + sql.append(" SELECT ").append(DatabaseSchema.UID_COLUMN).append(",").append(DatabaseSchema.TOKEN_COLUMN) + .append(",").append(DatabaseSchema.CREATION_DATE_COLUMN).append(" FROM ") + .append(DatabaseSchema.SCHEMA_NAME + "." + DatabaseSchema.TABLE_USER_TOKEN) + .append(" WHERE " + DatabaseSchema.CREATION_DATE_COLUMN + " <= ?"); + + return sql.toString(); + } + + /** + * Prepares the Statement setting the year and month. + */ + @Override + protected PreparedStatement prepareStatement(Connection connection) throws SQLException { + + PreparedStatement pStmt = connection.prepareStatement(getSQLStatement()); + + Timestamp time = new Timestamp(this.beforeDate.getTime()); + + pStmt.setTimestamp(1, time); + + return pStmt; + } + + @Override + protected Map getRow(ResultSet rs) throws SQLException { + + Map row = new HashMap(3); + row.put(DatabaseSchema.UID_COLUMN, rs.getString(DatabaseSchema.UID_COLUMN)); + row.put(DatabaseSchema.TOKEN_COLUMN, rs.getString(DatabaseSchema.TOKEN_COLUMN)); + row.put(DatabaseSchema.CREATION_DATE_COLUMN, rs.getTimestamp(DatabaseSchema.CREATION_DATE_COLUMN)); + + return row; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ds/UserTokenDao.java b/console/src/main/java/org/georchestra/console/ds/UserTokenDao.java new file mode 100644 index 0000000000..68d548a7f0 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ds/UserTokenDao.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ds; + +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.sql.DataSource; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.ds.DataServiceException; +import org.georchestra.lib.sqlcommand.DataCommand; +import org.georchestra.lib.sqlcommand.DataCommandException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ldap.NameNotFoundException; + +/** + * Maintains the tokens generated when the "Lost password use case" is executed. + * + * @author Mauricio Pazos + * + */ +public class UserTokenDao { + + private static final Log LOG = LogFactory.getLog(UserTokenDao.class.getName()); + + @Autowired + private DataSource dataSource; + + /** + * Inserts the new association uid-token. + * + * @param uid user identifier + * @param token token + * @throws DataServiceException + */ + public void insertToken(String uid, String token) throws DataServiceException { + Calendar cal = Calendar.getInstance(); + Date date = cal.getTime(); + Timestamp currentDay = new Timestamp(date.getTime()); + + Map row = new HashMap(3); + row.put(DatabaseSchema.UID_COLUMN, uid); + row.put(DatabaseSchema.TOKEN_COLUMN, token); + row.put(DatabaseSchema.CREATION_DATE_COLUMN, currentDay); + + InsertUserTokenCommand cmd = new InsertUserTokenCommand(); + cmd.setRowValues(row); + executeCmd(cmd, "Failed to insert the uid,token"); + } + + /** + * Searches the user_token association which match with the provided token. + * + * @param token + * @return uid + * + * @throws DataServiceException + * @throws NameNotFoundException + */ + public String findUserByToken(String token) throws DataServiceException, NameNotFoundException { + QueryByTokenCommand cmd = new QueryByTokenCommand(); + cmd.setToken(token); + executeCmd(cmd, "UserTokenDao.findUserByToken"); + + List> result = cmd.getResult(); + + if (result.isEmpty()) { + throw new NameNotFoundException("the token " + token + " wasn't found."); + } + + String uid = (String) result.get(0).get(DatabaseSchema.UID_COLUMN); + return uid; + } + + public List> findBeforeDate(Date expired) throws DataServiceException { + QueryUserTokenExpiredCommand cmd = new QueryUserTokenExpiredCommand(); + cmd.setBeforeDate(expired); + executeCmd(cmd, "UserTokenDao.delete"); + + List> result = cmd.getResult(); + return result; + } + + public boolean exist(String uid) throws DataServiceException { + QueryByUidCommand cmd = new QueryByUidCommand(); + cmd.setUid(uid); + executeCmd(cmd, "UserTokenDao.exist"); + + List> result = cmd.getResult(); + return !result.isEmpty(); + } + + public void delete(String uid) throws DataServiceException { + DeleteUserTokenCommand cmd = new DeleteUserTokenCommand(); + cmd.setUid(uid); + executeCmd(cmd, "UserTokenDao.delete"); + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + private void executeCmd(DataCommand cmd, String logMsg) throws DataServiceException { + try { + cmd.setDataSource(dataSource); + cmd.execute(); + } catch (DataCommandException e) { + LOG.error(logMsg, e); + throw new DataServiceException(e); + } + } +} diff --git a/console/src/main/java/org/georchestra/console/dto/EventType.java b/console/src/main/java/org/georchestra/console/dto/EventType.java new file mode 100644 index 0000000000..a489337387 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/dto/EventType.java @@ -0,0 +1,10 @@ +package org.georchestra.console.dto; + +/** + * Type discriminator for application events + *

+ * TODO: dead code! + */ +public enum EventType { + CREATED, MODIFIED, DELETED +} \ No newline at end of file diff --git a/console/src/main/java/org/georchestra/console/dto/SimpleAccount.java b/console/src/main/java/org/georchestra/console/dto/SimpleAccount.java new file mode 100644 index 0000000000..ce2a5137a4 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/dto/SimpleAccount.java @@ -0,0 +1,112 @@ +package org.georchestra.console.dto; + +import java.time.LocalDate; + +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.UserSchema; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; + +public class SimpleAccount { + + @JsonProperty(UserSchema.UID_KEY) + private String uid; + + @JsonProperty(UserSchema.GIVEN_NAME_KEY) + private String givenName; + + @JsonProperty(UserSchema.SURNAME_KEY) + private String surname; + + @JsonProperty(UserSchema.ORG_KEY) + private String orgName; + + @JsonProperty(UserSchema.ORG_ID_KEY) + private String orgId; + + @JsonProperty(UserSchema.MAIL_KEY) + private String email; + + @JsonProperty(UserSchema.PENDING) + private boolean pending; + + @JsonProperty(UserSchema.PRIVACY_POLICY_AGREEMENT_DATE_KEY) + @JsonSerialize(using = ToStringSerializer.class) + private LocalDate privacyPolicyAgreementDate; + + public SimpleAccount(Account account) { + this.uid = account.getUid(); + this.givenName = account.getGivenName(); + this.surname = account.getSurname(); + this.orgId = account.getOrg(); + this.email = account.getEmail(); + this.pending = account.isPending(); + this.privacyPolicyAgreementDate = account.getPrivacyPolicyAgreementDate(); + } + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public String getGivenName() { + return givenName; + } + + public void setGivenName(String givenName) { + this.givenName = givenName; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + + public String getOrgName() { + return orgName; + } + + public void setOrgName(String orgName) { + this.orgName = orgName; + } + + public String getOrgId() { + return orgId; + } + + public void setOrgId(String orgId) { + this.orgId = orgId; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public boolean isPending() { + return pending; + } + + public void setPending(boolean pending) { + this.pending = pending; + } + + public LocalDate getPrivacyPolicyAgreementDate() { + return this.privacyPolicyAgreementDate; + } + + public void setPrivacyPolicyAgreementDate(LocalDate privacyPolicyAgreementDate) { + this.privacyPolicyAgreementDate = privacyPolicyAgreementDate; + } +} diff --git a/console/src/main/java/org/georchestra/console/mailservice/Email.java b/console/src/main/java/org/georchestra/console/mailservice/Email.java new file mode 100644 index 0000000000..c1959bb865 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/mailservice/Email.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.mailservice; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; + +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import javax.servlet.ServletContext; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.commons.configuration.GeorchestraConfiguration; + +public class Email { + + protected static final Log LOG = LogFactory.getLog(Email.class.getName()); + + private String smtpHost; + private int smtpPort; + private boolean emailHtml; + private String replyTo; + private String from; + private String bodyEncoding; + private String subjectEncoding; + private String templateEncoding; + private List recipients; + private String subject; + private String emailBody; + + private String publicUrl; + private String instanceName; + private GeorchestraConfiguration georConfig; + private ServletContext servletContext; + + public Email(List recipients, String emailSubject, String smtpHost, int smtpPort, boolean emailHtml, + String replyTo, String from, String bodyEncoding, String subjectEncoding, String templateEncoding, + String fileTemplate, ServletContext servletContext, GeorchestraConfiguration georConfig, String publicUrl, + String instanceName) { + + this.recipients = recipients; + this.subject = emailSubject; + this.smtpHost = smtpHost; + this.smtpPort = smtpPort; + this.emailHtml = emailHtml; + this.replyTo = replyTo; + this.from = from; + this.bodyEncoding = bodyEncoding; + this.subjectEncoding = subjectEncoding; + this.templateEncoding = templateEncoding; + this.georConfig = georConfig; + this.publicUrl = publicUrl; + this.instanceName = instanceName; + this.servletContext = servletContext; + // Load template from filesystem + this.emailBody = this.loadBody(fileTemplate); + } + + public void set(String key, String value) { + this.emailBody = this.emailBody.replaceAll("\\{" + key + "\\}", value); + } + + @Override + public String toString() { + return "Email{" + "smtpHost='" + smtpHost + '\'' + ", smtpPort=" + smtpPort + ", emailHtml='" + emailHtml + '\'' + + ", replyTo='" + replyTo + '\'' + ", from='" + from + '\'' + ", bodyEncoding='" + bodyEncoding + '\'' + + ", subjectEncoding='" + subjectEncoding + '\'' + ", recipients=" + + recipients.stream().collect(Collectors.joining(",")) + ", subject='" + subject + '\'' + + ", emailBody='" + emailBody + '\'' + '}'; + } + + /** + * Loads the body template. + * + * if available, the templates will be resolved from the geOrchestra datadir, + * and if not defined, a one inside the webapp will be used. + * + * @param fileName the filename to open, without the path to it. + * @return + * @throws IOException + */ + private String loadBody(final String fileName) { + + if ((georConfig != null) && (georConfig.activated())) { + try { + File fileTmpl = Paths.get(georConfig.getContextDataDir(), "templates", fileName).toFile(); + + return FileUtils.readFileToString(fileTmpl, templateEncoding); + } catch (IOException e) { + LOG.error("Unable to get the template from geOrchestra datadir. " + + "Falling back on the default template provided by the webapp.", e); + } + } + /* Trying to resolve the templates from inside the webapp */ + String tmplFromWebapp = this.servletContext + .getRealPath(Paths.get("/WEB-INF", "templates", fileName).toString()); + + String body = null; + try { + body = FileUtils.readFileToString(new File(tmplFromWebapp), templateEncoding); + } catch (IOException e) { + LOG.error(e); + } + return body; + } + + public MimeMessage send() throws MessagingException { + return this.send(true); + } + + public MimeMessage send(boolean reallySend) throws MessagingException { + + // Replace {publicUrl} token with the configured public URL + this.emailBody = this.emailBody.replaceAll("\\{publicUrl\\}", publicUrl); + this.emailBody = this.emailBody.replaceAll("\\{instanceName\\}", instanceName); + LOG.debug("body: " + this.emailBody); + + final Session session = Session.getInstance(System.getProperties(), null); + session.getProperties().setProperty("mail.smtp.host", smtpHost); + session.getProperties().setProperty("mail.smtp.port", (new Integer(smtpPort)).toString()); + + final MimeMessage message = new MimeMessage(session); + + if (isValidEmailAddress(from)) { + message.setFrom(new InternetAddress(from)); + } + for (String recipient : recipients) { + if (!isValidEmailAddress(recipient)) + throw new AddressException("Invalid recipient : " + recipient); + message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient)); + } + if (isValidEmailAddress(replyTo)) { + message.setReplyTo(new InternetAddress[] { new InternetAddress(replyTo) }); + } + + message.setSubject(subject, subjectEncoding); + + if (this.emailBody != null) { + if (emailHtml) { + message.setContent(this.emailBody, "text/html; charset=" + bodyEncoding); + } else { + message.setContent(this.emailBody, "text/plain; charset=" + bodyEncoding); + } + } + + // Finally send the message + if (reallySend) + Transport.send(message); + LOG.debug("email has been sent to:\n" + recipients.stream().collect(Collectors.joining(","))); + return message; + } + + private static boolean isValidEmailAddress(String address) { + if (address == null) { + return false; + } + + boolean hasCharacters = address.trim().length() > 0; + boolean hasAt = address.contains("@"); + + if (!hasCharacters || !hasAt) + return false; + + String[] parts = address.trim().split("@", 2); + + boolean mainPartNotEmpty = parts[0].trim().length() > 0; + boolean hostPartNotEmpty = parts[1].trim().length() > 0; + return mainPartNotEmpty && hostPartNotEmpty; + } + +} \ No newline at end of file diff --git a/console/src/main/java/org/georchestra/console/mailservice/EmailFactory.java b/console/src/main/java/org/georchestra/console/mailservice/EmailFactory.java new file mode 100644 index 0000000000..5c90c87196 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/mailservice/EmailFactory.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.mailservice; + +import static java.util.Collections.singletonList; + +import java.util.List; + +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.internet.MimeMessage; +import javax.servlet.ServletContext; + +import org.georchestra.commons.configuration.GeorchestraConfiguration; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Manage e-mails required for this application + */ +public class EmailFactory { + + private String smtpHost; + private int smtpPort; + private boolean emailHtml; + private String replyTo; + private String from; + private String bodyEncoding; + private String subjectEncoding; + private String templateEncoding; + + @Autowired + private GeorchestraConfiguration georConfig; + + private String accountWasCreatedEmailFile; + private String accountWasCreatedEmailSubject; + + private String accountCreationInProcessEmailFile; + private String accountCreationInProcessEmailSubject; + + private String newAccountRequiresModerationEmailFile; + private String newAccountRequiresModerationEmailSubject; + + private String changePasswordEmailFile; + private String changePasswordEmailSubject; + + private String accountUidRenamedEmailFile; + private String accountUidRenamedEmailSubject; + + private String newAccountNotificationEmailFile; + private String newAccountNotificationEmailSubject; + + private String publicUrl; + private String instanceName; + + private String administratorEmail; + + public void sendAccountWasCreatedEmail(ServletContext servletContext, String recipient, String userName, String uid) + throws MessagingException { + sendAccountWasCreatedEmail(servletContext, recipient, userName, uid, true); + } + + public void sendAccountWasCreatedEmail(ServletContext servletContext, String recipient, String userName, String uid, + boolean reallySend) throws MessagingException { + Email email = new Email(singletonList(recipient), this.accountWasCreatedEmailSubject, this.smtpHost, + this.smtpPort, this.emailHtml, this.replyTo, this.from, this.bodyEncoding, this.subjectEncoding, + this.templateEncoding, this.accountWasCreatedEmailFile, servletContext, this.georConfig, this.publicUrl, + this.instanceName); + email.set("name", userName); + email.set("uid", uid); + email.send(reallySend); + } + + /** + * e-mail to the user to inform the account requires the moderator's validation + */ + public void sendAccountCreationInProcessEmail(ServletContext servletContext, String recipient, String userName, + String uid) throws MessagingException { + sendAccountCreationInProcessEmail(servletContext, recipient, userName, uid, true); + } + + public void sendAccountCreationInProcessEmail(ServletContext servletContext, String recipient, String userName, + String uid, boolean reallySend) throws MessagingException { + + Email email = new Email(singletonList(recipient), this.accountCreationInProcessEmailSubject, this.smtpHost, + this.smtpPort, this.emailHtml, this.replyTo, this.from, this.bodyEncoding, this.subjectEncoding, + this.templateEncoding, this.accountCreationInProcessEmailFile, servletContext, this.georConfig, + this.publicUrl, this.instanceName); + email.set("name", userName); + email.set("uid", uid); + email.send(reallySend); + } + + /** + * emails to the moderator to inform that a new user is waiting authorization. + */ + public void sendNewAccountRequiresModerationEmail(ServletContext servletContext, List recipients, + String userName, String uid, String userEmail, String userOrg) throws MessagingException { + sendNewAccountRequiresModerationEmail(servletContext, recipients, userName, uid, userEmail, userOrg, true); + } + + public void sendNewAccountRequiresModerationEmail(ServletContext servletContext, List recipients, + String userName, String uid, String userEmail, String userOrg, boolean reallySend) + throws MessagingException { + + Email email = new Email(recipients, this.newAccountRequiresModerationEmailSubject, this.smtpHost, this.smtpPort, + this.emailHtml, userEmail, // Reply-to + this.from, // From + this.bodyEncoding, this.subjectEncoding, this.templateEncoding, + this.newAccountRequiresModerationEmailFile, servletContext, this.georConfig, this.publicUrl, + this.instanceName); + email.set("name", userName); + email.set("uid", uid); + email.set("email", userEmail); + email.set("org", userOrg); + email.send(reallySend); + } + + public void sendChangePasswordEmail(ServletContext servletContext, String recipient, String userName, String uid, + String url) throws MessagingException { + sendChangePasswordEmail(servletContext, recipient, userName, uid, url, true); + } + + public void sendChangePasswordEmail(ServletContext servletContext, String recipient, String userName, String uid, + String url, boolean reallySend) throws MessagingException { + Email email = new Email(singletonList(recipient), this.changePasswordEmailSubject, this.smtpHost, this.smtpPort, + this.emailHtml, this.replyTo, this.from, this.bodyEncoding, this.subjectEncoding, this.templateEncoding, + this.changePasswordEmailFile, servletContext, this.georConfig, this.publicUrl, this.instanceName); + email.set("name", userName); + email.set("uid", uid); + email.set("url", url); + email.send(reallySend); + } + + public void sendAccountUidRenamedEmail(ServletContext servletContext, String recipient, String userName, String uid) + throws MessagingException { + sendAccountUidRenamedEmail(servletContext, recipient, userName, uid, true); + } + + public void sendAccountUidRenamedEmail(ServletContext servletContext, String recipient, String userName, String uid, + boolean reallySend) throws MessagingException { + + Email email = new Email(singletonList(recipient), this.accountUidRenamedEmailSubject, this.smtpHost, + this.smtpPort, this.emailHtml, this.replyTo, this.from, this.bodyEncoding, this.subjectEncoding, + this.templateEncoding, this.accountUidRenamedEmailFile, servletContext, this.georConfig, this.publicUrl, + this.instanceName); + email.set("name", userName); + email.set("uid", uid); + email.send(reallySend); + } + + public void sendNewAccountNotificationEmail(ServletContext servletContext, List recipients, String userName, + String uid, String userEmail, String userOrg) throws MessagingException { + sendNewAccountNotificationEmail(servletContext, recipients, userName, uid, userEmail, userOrg, true); + } + + public void sendNewAccountNotificationEmail(ServletContext servletContext, List recipients, String userName, + String uid, String userEmail, String userOrg, boolean reallySend) throws MessagingException { + + Email email = new Email(recipients, this.newAccountNotificationEmailSubject, this.smtpHost, this.smtpPort, + this.emailHtml, userEmail, // Reply-to + this.from, this.bodyEncoding, this.subjectEncoding, this.templateEncoding, + this.newAccountNotificationEmailFile, servletContext, this.georConfig, this.publicUrl, + this.instanceName); + email.set("name", userName); + email.set("uid", uid); + email.set("email", userEmail); + if (userOrg == null) { + userOrg = ""; + } + email.set("org", userOrg); + email.send(reallySend); + } + + public MimeMessage createEmptyMessage() { + // Instanciate MimeMessage + final Session session = Session.getInstance(System.getProperties(), null); + session.getProperties().setProperty("mail.smtp.host", this.smtpHost); + session.getProperties().setProperty("mail.smtp.port", (new Integer(this.smtpPort)).toString()); + return new MimeMessage(session); + } + + /* + * Setters for unit tests + */ + + public void setSmtpHost(String smtpHost) { + this.smtpHost = smtpHost; + } + + public void setSmtpPort(int smtpPort) { + this.smtpPort = smtpPort; + } + + public void setEmailHtml(boolean emailHtml) { + this.emailHtml = emailHtml; + } + + public void setReplyTo(String replyTo) { + this.replyTo = replyTo; + } + + public void setFrom(String from) { + this.from = from; + } + + public void setBodyEncoding(String bodyEncoding) { + this.bodyEncoding = bodyEncoding; + } + + public void setSubjectEncoding(String subjectEncoding) { + this.subjectEncoding = subjectEncoding; + } + + public void setTemplateEncoding(String templateEncoding) { + this.templateEncoding = templateEncoding; + } + + public void setGeorConfig(GeorchestraConfiguration georConfig) { + this.georConfig = georConfig; + } + + public void setAccountWasCreatedEmailFile(String accountWasCreatedEmailFile) { + this.accountWasCreatedEmailFile = accountWasCreatedEmailFile; + } + + public void setAccountWasCreatedEmailSubject(String accountWasCreatedEmailSubject) { + this.accountWasCreatedEmailSubject = accountWasCreatedEmailSubject; + } + + public void setAccountCreationInProcessEmailFile(String accountCreationInProcessEmailFile) { + this.accountCreationInProcessEmailFile = accountCreationInProcessEmailFile; + } + + public void setAccountCreationInProcessEmailSubject(String accountCreationInProcessEmailSubject) { + this.accountCreationInProcessEmailSubject = accountCreationInProcessEmailSubject; + } + + public void setNewAccountRequiresModerationEmailFile(String newAccountRequiresModerationEmailFile) { + this.newAccountRequiresModerationEmailFile = newAccountRequiresModerationEmailFile; + } + + public void setNewAccountRequiresModerationEmailSubject(String newAccountRequiresModerationEmailSubject) { + this.newAccountRequiresModerationEmailSubject = newAccountRequiresModerationEmailSubject; + } + + public void setChangePasswordEmailFile(String changePasswordEmailFile) { + this.changePasswordEmailFile = changePasswordEmailFile; + } + + public void setChangePasswordEmailSubject(String changePasswordEmailSubject) { + this.changePasswordEmailSubject = changePasswordEmailSubject; + } + + public void setAccountUidRenamedEmailFile(String accountUidRenamedEmailFile) { + this.accountUidRenamedEmailFile = accountUidRenamedEmailFile; + } + + public void setAccountUidRenamedEmailSubject(String accountUidRenamedEmailSubject) { + this.accountUidRenamedEmailSubject = accountUidRenamedEmailSubject; + } + + public void setNewAccountNotificationEmailFile(String newAccountNotificationEmailFile) { + this.newAccountNotificationEmailFile = newAccountNotificationEmailFile; + } + + public void setNewAccountNotificationEmailSubject(String newAccountNotificationEmailSubject) { + this.newAccountNotificationEmailSubject = newAccountNotificationEmailSubject; + } + + public void setPublicUrl(String publicUrl) { + this.publicUrl = publicUrl; + } + + public void setInstanceName(String instanceName) { + this.instanceName = instanceName; + } + + public void setAdministratorEmail(String administratorEmail) { + this.administratorEmail = administratorEmail; + } +} diff --git a/console/src/main/java/org/georchestra/console/model/AdminLogEntry.java b/console/src/main/java/org/georchestra/console/model/AdminLogEntry.java new file mode 100644 index 0000000000..cc678a85ab --- /dev/null +++ b/console/src/main/java/org/georchestra/console/model/AdminLogEntry.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.model; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; + +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.vladmihalcea.hibernate.type.json.JsonBinaryType; + +@Entity +@Table(schema = "console", name = "admin_log") +@NamedQueries({ + @NamedQuery(name = "AdminLogEntry.findByTargetPageable", query = "SELECT l FROM AdminLogEntry l WHERE l.target = :target ORDER BY l.date DESC"), + @NamedQuery(name = "AdminLogEntry.myFindByTargets", query = "SELECT l FROM AdminLogEntry l WHERE l.target IN :targets ORDER BY l.date DESC") }) +@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) +public class AdminLogEntry { + + @Id + @SequenceGenerator(name = "admin_log_seq", schema = "console", sequenceName = "admin_log_seq", initialValue = 1, allocationSize = 1) + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "admin_log_seq") + @JsonIgnore + private long id; + private String admin; + private String target; + private AdminLogType type; + private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + + @Type(type = "jsonb") + @Column(columnDefinition = "jsonb") + private String changed; + + @Column(updatable = false, nullable = false) + @JsonIgnore + private Date date; + + public AdminLogEntry() { + } + + public AdminLogEntry(String admin, String target, AdminLogType type, Date date) { + this.admin = admin; + this.target = target; + this.type = type; + this.date = date; + } + + /** + * Constructor + * + * @param admin String that realized action + * @param target String user concerned by this action + * @param type AdminLogType to identify action + * @param date Date of log + * @param changed String to save changed as JSON + */ + public AdminLogEntry(String admin, String target, AdminLogType type, Date date, String changed) { + this.admin = admin; + this.target = target; + this.type = type; + this.date = date; + this.changed = changed; + } + + public String getChanged() { + return changed; + } + + public void setChanged(String changed) { + this.changed = changed; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getAdmin() { + return admin; + } + + public void setAdmin(String admin) { + this.admin = admin; + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public AdminLogType getType() { + return type; + } + + public void setType(AdminLogType type) { + this.type = type; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + @JsonGetter("date") + public String getFormattedDate() { + return AdminLogEntry.dateFormat.format(this.date); + } + +} diff --git a/console/src/main/java/org/georchestra/console/model/AdminLogType.java b/console/src/main/java/org/georchestra/console/model/AdminLogType.java new file mode 100644 index 0000000000..1a805e955c --- /dev/null +++ b/console/src/main/java/org/georchestra/console/model/AdminLogType.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.model; + +public enum AdminLogType { + /** + * Initial events logs types Aborted since 10/2019 + * + * Databases table with logs before 10/2019 search the code witch correspond to + * this old type. Keep this types to retrieve old logs and avoid server error. + */ + ACCOUNT_MODERATION("Account moderation"), SYSTEM_ROLE_CHANGE("System role"), OTHER_ROLE_CHANGE("Other role"), + LDAP_ATTRIBUTE_CHANGE("User attributes"), + + /** + * New events logs types insert since 10/2019 + * + * Added to get more informations about logs and used to identify the real + * modification for each action + */ + CUSTOM_ROLE_ADDED("A custom role was added"), CUSTOM_ROLE_REMOVED("A custom role was removed"), + + EMAIL_SENT("Email sent"), EMAIL_RECOVERY_SENT("Email to recover password sent"), + + ORG_ATTRIBUTE_CHANGED("Attribute was changed for an org"), + + ORG_CREATED("An org was created"), ORG_DELETED("An org was deleted"), + + PENDING_ORG_ACCEPTED("Pending org was accepted"), PENDING_ORG_CREATED("Pending org was created"), + PENDING_ORG_REFUSED("Pending org was refused"), + + PENDING_USER_ACCEPTED("Pending user was accepted"), PENDING_USER_CREATED("Pending user was created"), + PENDING_USER_REFUSED("Pending user was refused"), + + ROLE_CREATED("A role was created"), ROLE_DELETED("A role was deleted"), + + SYSTEM_ROLE_REMOVED("System role was removed"), SYSTEM_ROLE_ADDED("System role was added"), + + USER_ATTRIBUTE_CHANGED("Attribute was changed for a user"), + + USER_CREATED("A user was created"), USER_DELETED("A user was deleted"), + + USER_PASSWORD_CHANGED("Password was changed for a user"); + + private String name; + + private AdminLogType(String name) { + this.name = name; + } + + public String toString() { + return name; + } +} diff --git a/console/src/main/java/org/georchestra/console/model/Attachment.java b/console/src/main/java/org/georchestra/console/model/Attachment.java new file mode 100644 index 0000000000..327f7e593f --- /dev/null +++ b/console/src/main/java/org/georchestra/console/model/Attachment.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.model; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; + +import org.json.JSONException; +import org.json.JSONObject; + +@Entity +@Table(schema = "console", name = "admin_attachments") +public class Attachment { + + @Id + @SequenceGenerator(name = "admin_attachments_id_seq", schema = "console", sequenceName = "admin_attachments_id_seq", initialValue = 1, allocationSize = 1) + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "admin_attachments_id_seq") + private long id; + private String name; + @Lob + private byte[] content; + private String mimeType; + + public Attachment() { + } + + public Attachment(String name, String mimeType, byte[] content) { + this.name = name; + this.mimeType = mimeType; + this.content = content; + } + + public JSONObject toJSON() throws JSONException { + JSONObject res = new JSONObject(); + res.put("id", this.getId()); + res.put("name", this.getName()); + res.put("mimeType", this.getMimeType()); + res.put("size", this.content.length); + return res; + } + + /* + * Generic getter, setter + */ + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public byte[] getContent() { + return content; + } + + public void setContent(byte[] content) { + this.content = content; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMimeType() { + return mimeType; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + +} diff --git a/console/src/main/java/org/georchestra/console/model/DelegationEntry.java b/console/src/main/java/org/georchestra/console/model/DelegationEntry.java new file mode 100644 index 0000000000..70fdc80317 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/model/DelegationEntry.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.model; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.commons.lang3.ArrayUtils; +import org.hibernate.annotations.Type; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +@Entity +@Table(schema = "console", name = "delegations") +public class DelegationEntry { + + @Id + private String uid; + @Type(type = "org.georchestra.console.model.PostGresArrayStringType") + @Column(name = "orgs", columnDefinition = "character varying[]") + private String[] orgs; + @Type(type = "org.georchestra.console.model.PostGresArrayStringType") + @Column(name = "roles", columnDefinition = "character varying[]") + private String[] roles; + + public DelegationEntry() { + } + + public DelegationEntry(String uid, String[] orgs, String[] roles) { + this.uid = uid; + this.orgs = orgs; + this.roles = roles; + } + + synchronized public void removeOrg(String orgToRemove) { + this.orgs = ArrayUtils.removeElement(this.orgs, orgToRemove); + } + + synchronized public void removeRole(String roleToremove) { + this.roles = ArrayUtils.removeElement(this.roles, roleToremove); + } + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public String[] getOrgs() { + return orgs; + } + + public void setOrgs(String[] orgs) { + this.orgs = orgs; + } + + public String[] getRoles() { + return roles; + } + + public void setRoles(String[] roles) { + this.roles = roles; + } + + public JSONObject toJSON() throws JSONException { + JSONObject res = new JSONObject(); + res.put("uid", this.uid); + res.put("orgs", new JSONArray(this.orgs)); + res.put("roles", new JSONArray(this.roles)); + return res; + } +} diff --git a/console/src/main/java/org/georchestra/console/model/EmailEntry.java b/console/src/main/java/org/georchestra/console/model/EmailEntry.java new file mode 100644 index 0000000000..1d9a366805 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/model/EmailEntry.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.model; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +@Entity +@Table(schema = "console", name = "admin_emails") +public class EmailEntry { + + @Id + @SequenceGenerator(name = "admin_emails_id_seq", schema = "console", sequenceName = "admin_emails_id_seq", initialValue = 1, allocationSize = 1) + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "admin_emails_id_seq") + private long id; + private String sender; + private String recipient; + @Column(columnDefinition = "TEXT") + private String subject; + private Date date; + @Column(columnDefinition = "TEXT") + private String body; + + @ManyToMany(targetEntity = Attachment.class, fetch = FetchType.EAGER) + @JoinTable(schema = "console", name = "admin_emails_attachments") + private List attachments; + + public EmailEntry() { + } + + public EmailEntry(long id, String sender, String recipient, String subject, Date date, String body, + List attachments) { + this.id = id; + this.sender = sender; + this.recipient = recipient; + this.subject = subject; + this.date = date; + this.body = body; + this.attachments = attachments; + } + + public JSONObject toJSON() throws JSONException { + JSONObject res = new JSONObject(); + res.put("id", this.getId()); + res.put("sender", this.getSender()); + res.put("recipient", this.getRecipient()); + res.put("subject", this.getSubject()); + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + res.put("date", dateFormat.format(this.getDate())); + res.put("body", this.getBody()); + JSONArray array = new JSONArray(); + for (Attachment att : this.getAttachments()) + array.put(att.toJSON()); + res.put("attachments", array); + return res; + } + + /* + * Generic getter, setter + */ + public long getId() { + return this.id; + } + + public void setId(long id) { + this.id = id; + } + + public String getSender() { + return this.sender; + } + + public String getRecipient() { + return this.recipient; + } + + public String getSubject() { + return this.subject; + } + + public String getBody() { + return this.body; + } + + public List getAttachments() { + return this.attachments; + } + + public void setSender(String sender) { + this.sender = sender; + } + + public void setRecipient(String recipient) { + this.recipient = recipient; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public void setBody(String body) { + this.body = body; + } + + public void setAttachments(List attachments) { + this.attachments = attachments; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + +} diff --git a/console/src/main/java/org/georchestra/console/model/EmailTemplate.java b/console/src/main/java/org/georchestra/console/model/EmailTemplate.java new file mode 100644 index 0000000000..78be12b225 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/model/EmailTemplate.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.model; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; + +import org.json.JSONException; +import org.json.JSONObject; + +@Entity +@Table(schema = "console", name = "email_template") +public class EmailTemplate { + + @Id + @SequenceGenerator(name = "email_template_id_seq", schema = "console", sequenceName = "email_template_id_seq", initialValue = 1, allocationSize = 1) + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "email_template_id_seq") + private long id; + private String name; + @Column(columnDefinition = "TEXT") + private String content; + + public EmailTemplate() { + } + + public EmailTemplate(long id, String name, String content) { + this.id = id; + this.name = name; + this.content = content; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public JSONObject toJSON() throws JSONException { + JSONObject res = new JSONObject(); + res.put("id", this.getId()); + res.put("name", this.getName()); + res.put("content", this.getContent()); + return res; + } +} diff --git a/console/src/main/java/org/georchestra/console/model/PostGresArrayStringType.java b/console/src/main/java/org/georchestra/console/model/PostGresArrayStringType.java new file mode 100644 index 0000000000..6da9b3e1fc --- /dev/null +++ b/console/src/main/java/org/georchestra/console/model/PostGresArrayStringType.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.model; + +import java.io.Serializable; +import java.sql.Array; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.usertype.UserType; + +public class PostGresArrayStringType implements UserType { + protected static final int[] SQL_TYPES = { Types.ARRAY }; + + @Override + public final Object deepCopy(final Object value) throws HibernateException { + return value; + } + + @Override + public final boolean isMutable() { + return false; + } + + @Override + public final Object assemble(final Serializable arg0, final Object arg1) throws HibernateException { + // TODO Auto-generated method stub + return null; + } + + @Override + public final Serializable disassemble(final Object arg0) throws HibernateException { + // TODO Auto-generated method stub + return null; + } + + @Override + public final boolean equals(final Object x, final Object y) throws HibernateException { + if (x == y) { + return true; + } else if (x == null || y == null) { + return false; + } else { + return x.equals(y); + } + } + + @Override + public final int hashCode(final Object x) throws HibernateException { + return x.hashCode(); + } + + @Override + public final Object replace(final Object original, final Object target, final Object owner) + throws HibernateException { + return original; + } + + @Override + public int[] sqlTypes() { + return SQL_TYPES; + } + + @Override + public Class returnedClass() { + return String[].class; + } + + @Override + public final Object nullSafeGet(final ResultSet resultSet, final String[] names, final SessionImplementor session, + final Object owner) throws HibernateException, SQLException { + if (resultSet.wasNull()) { + return null; + } + + String[] array = (String[]) resultSet.getArray(names[0]).getArray(); + return array; + } + + @Override + public final void nullSafeSet(final PreparedStatement statement, final Object value, final int index, + final SessionImplementor session) throws HibernateException, SQLException { + + if (value == null) { + statement.setNull(index, SQL_TYPES[0]); + } else { + String[] castObject = (String[]) value; + Array array = session.connection().createArrayOf("text", castObject); + statement.setArray(index, array); + } + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/AreaController.java b/console/src/main/java/org/georchestra/console/ws/AreaController.java new file mode 100644 index 0000000000..2b01868c6c --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/AreaController.java @@ -0,0 +1,85 @@ +package org.georchestra.console.ws; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; + +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.util.StreamUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * A simple controller to serve an area.json file from your datadir. + */ +@Controller +public class AreaController { + + @Value("${AreasUrl:area.geojson}") + private String areasUrl; + + @Value("${georchestra.datadir:/etc/georchestra}") + private String datadir; + + /** + * serve a json file put in the datadir or redirect to a url if AreaUrl in the + * config is set to a url. + * + * @return json or rediurect to the resource if area is an URL + * @throws IOException + */ + @GetMapping(value = "/public/area.geojson", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @ResponseBody + public String serveArea(HttpServletResponse response) throws IOException { + // if arealUrl in config is an http endpoint, then send it directly. + if (isURL(areasUrl)) { + response.sendRedirect(areasUrl); + return ""; + } + File areaJsonFile = lookForAreaUrl(); + try (InputStream stream = new FileInputStream(areaJsonFile)) { + String jsonString = StreamUtils.copyToString(stream, StandardCharsets.UTF_8); + return new JSONObject(jsonString).toString(); + } catch (IOException ioe) { + // in case there are any errors with the area file, pretend it didn't exist. + response.setStatus(404); + return "{\"error\": \"area.geojson not found\"}"; + } catch (JSONException ex) { + response.setStatus(500); + return "{\"error\": \"specifed file (area.geojson) could not be parsed server side\"}"; + } + } + + /** + * try to be a bit permissive in what path we accept for area.json file, could + * be just `area.json` could be /the/whole/path/to/datadir/ + */ + private File lookForAreaUrl() { + String[] possiblePath = { areasUrl, Paths.get(datadir, areasUrl).toString(), + Paths.get(datadir, "/console/", areasUrl).toString(), }; + File f = null; + for (String p : possiblePath) { + f = new File(p); + if (f.exists()) { + return f; + } + } + return f; + } + + /** + * Check if the path given in AreaUrl is an actual URL or a file + */ + private boolean isURL(String possibleUrl) { + return possibleUrl.startsWith("http"); + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/GlobalExceptionHandler.java b/console/src/main/java/org/georchestra/console/ws/GlobalExceptionHandler.java new file mode 100644 index 0000000000..1ba9bb0612 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/GlobalExceptionHandler.java @@ -0,0 +1,43 @@ +package org.georchestra.console.ws; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.console.ws.backoffice.utils.Response; +import org.georchestra.console.ws.backoffice.utils.ResponseUtil; +import org.springframework.http.HttpStatus; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ControllerAdvice +public class GlobalExceptionHandler { + + private static final Log LOG = LogFactory.getLog(GlobalExceptionHandler.class.getName()); + + @ExceptionHandler(NameNotFoundException.class) + @ResponseStatus(value = HttpStatus.NOT_FOUND) + @ResponseBody + public Response handleNameNotFoundException(NameNotFoundException e) { + LOG.info(e.getMessage()); + return ResponseUtil.failure(e.getMessage()); + } + + @ExceptionHandler(AccessDeniedException.class) + @ResponseStatus(value = HttpStatus.FORBIDDEN) + @ResponseBody + public Response handleAccessDeniedException(AccessDeniedException e) { + LOG.debug(e.getMessage()); + return ResponseUtil.failure(e.getMessage()); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) + @ResponseBody + public Response handleException(Exception e) { + LOG.error(e.getMessage(), e); + return ResponseUtil.failure(e.getMessage()); + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/HomeController.java b/console/src/main/java/org/georchestra/console/ws/HomeController.java new file mode 100644 index 0000000000..39c58a1409 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/HomeController.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws; + +import static org.georchestra.commons.security.SecurityHeaders.SEC_ROLES; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.commons.security.SecurityHeaders; +import org.georchestra.console.bs.ExpiredTokenManagement; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * Displays the home page, also intercepts some assets. + * + * + * @author Mauricio Pazos, Pierre Mauduit + * + */ +@Controller +public class HomeController { + + private static final Log LOG = LogFactory.getLog(HomeController.class.getName()); + private ExpiredTokenManagement tokenManagement; + + @Value("${publicContextPath:/console}") + private String publicContextPath; + + @Autowired + private ServletContext context; + + @Autowired + public HomeController(ExpiredTokenManagement tokenManagment) { + if (LOG.isDebugEnabled()) { + LOG.debug("home controller initialization"); + } + + this.tokenManagement = tokenManagment; + this.tokenManagement.start(); + } + + @RequestMapping(value = "/") + public void root(HttpServletRequest request, HttpServletResponse response) throws IOException { + + String roles = SecurityHeaders.decode(request.getHeader(SEC_ROLES)); + + if (roles != null) { + String redirectUrl; + List rolesList = Arrays.asList(roles.split(";")); + + if (rolesList.contains("ROLE_SUPERUSER") || rolesList.contains("ROLE_ORGADMIN")) { + redirectUrl = "/manager/"; + } else { + redirectUrl = "/account/userdetails"; + } + if (LOG.isDebugEnabled()) { + LOG.debug("root page request -> redirection to " + publicContextPath + redirectUrl); + } + response.sendRedirect(publicContextPath + redirectUrl); + } else { + // redirect to CAS + response.sendRedirect(publicContextPath + "/account/userdetails?login"); + } + } + + @RequestMapping(value = "/manager/") + public String consoleHome(HttpServletRequest request) throws IOException { + return "managerUi"; + } + + public void setPublicContextPath(String publicContextPath) { + this.publicContextPath = publicContextPath; + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/delegations/DelegationController.java b/console/src/main/java/org/georchestra/console/ws/backoffice/delegations/DelegationController.java new file mode 100644 index 0000000000..983e47d05f --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/delegations/DelegationController.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.delegations; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.console.dao.DelegationDao; +import org.georchestra.console.model.DelegationEntry; +import org.georchestra.console.ws.backoffice.utils.ResponseUtil; +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.roles.RoleDao; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.AccountDao; +import org.georchestra.lib.file.FileUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +@Controller +public class DelegationController { + + private static final Log LOG = LogFactory.getLog(DelegationController.class.getName()); + + private static final String BASE_MAPPING = "/private"; + private static final String REQUEST_MAPPING = BASE_MAPPING + "/delegation"; + + @Autowired + private DelegationDao delegationDao; + @Autowired + private RoleDao roleDao; + @Autowired + private AccountDao accountDao; + + @ExceptionHandler(Exception.class) + @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) + @ResponseBody + public String handleException(Exception e, HttpServletResponse response) throws IOException { + LOG.error(e.getMessage()); + ResponseUtil.buildResponse(response, ResponseUtil.buildResponseMessage(false, e.getMessage()), + HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + throw new IOException(e); + } + + @RequestMapping(value = REQUEST_MAPPING + + "/delegations", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + public String findAll() throws JSONException { + + // TODO filter if request came from delegated admin + Iterable delegations = this.delegationDao.findAll(); + + JSONArray res = new JSONArray(); + for (DelegationEntry delegation : delegations) + res.put(delegation.toJSON()); + + return res.toString(); + } + + @RequestMapping(value = REQUEST_MAPPING + + "/{uid}", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + public String findByUid(@PathVariable String uid) throws JSONException { + + // TODO test if uid correspond to connected user if request came from delegated + // admin + return this.delegationDao.findOne(uid).toJSON().toString(); + } + + @RequestMapping(value = REQUEST_MAPPING + + "/{uid}", method = RequestMethod.POST, produces = "application/json; charset=utf-8") + @ResponseBody + public String add(HttpServletRequest request, @PathVariable String uid) + throws JSONException, IOException, DataServiceException { + + // TODO deny if request came from delegated admin + // Parse Json + JSONObject json = new JSONObject(FileUtils.asString(request.getInputStream())); + + DelegationEntry delegation = new DelegationEntry(); + delegation.setUid(uid); + delegation.setOrgs(this.parseJSONArray(json.getJSONArray("orgs"))); + delegation.setRoles(this.parseJSONArray(json.getJSONArray("roles"))); + this.delegationDao.save(delegation); + Account account = accountDao.findByUID(uid); + this.roleDao.addUser("ORGADMIN", account); + + return delegation.toJSON().toString(); + } + + private String[] parseJSONArray(JSONArray array) throws JSONException { + List res = new LinkedList(); + for (int i = 0; i < array.length(); i++) { + res.add(array.getString(i)); + } + return res.toArray(new String[res.size()]); + } + + @RequestMapping(value = REQUEST_MAPPING + + "/{uid}", method = RequestMethod.DELETE, produces = "application/json; charset=utf-8") + @ResponseBody + public String delete(HttpServletRequest request, @PathVariable String uid) + throws JSONException, IOException, DataServiceException { + + // TODO deny if request came from delegated admin + this.delegationDao.delete(uid); + this.roleDao.deleteUser("ORGADMIN", accountDao.findByUID(uid)); + return new JSONObject().put("result", "ok").toString(); + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/log/LogController.java b/console/src/main/java/org/georchestra/console/ws/backoffice/log/LogController.java new file mode 100644 index 0000000000..652350bbfe --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/log/LogController.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.log; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.console.dao.AdminLogDao; +import org.georchestra.console.dao.AdvancedDelegationDao; +import org.georchestra.console.dao.DelegationDao; +import org.georchestra.console.model.AdminLogEntry; +import org.georchestra.console.model.DelegationEntry; +import org.georchestra.ds.orgs.OrgsDao; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class LogController { + + private static final Log LOG = LogFactory.getLog(LogController.class.getName()); + + private static final String BASE_MAPPING = "/private"; + private static final String REQUEST_MAPPING = BASE_MAPPING + "/admin_logs"; + private static GrantedAuthority ROLE_SUPERUSER = new SimpleGrantedAuthority("ROLE_SUPERUSER"); + + @Autowired + private AdminLogDao logDao; + + @Autowired + private DelegationDao delegationDao; + @Autowired + private OrgsDao orgsDao; + @Autowired + private AdvancedDelegationDao advancedDelegationDao; + + /** + * Returns array of logs using json syntax. + * + *

+     *     {"logs": [
+     *		{
+     *			"admin": "testadmin",
+     *			"date": "2016-03-22T15:26:21.087+0100",
+     *			"target": "testeditor",
+     *			"type": "Email sent"
+     *		},
+     *		{
+     *			"admin": "testadmin",
+     *			"date": "2016-03-21T17:50:09.258+0100",
+     *			"target": "joe",
+     *			"type": "Email sent"
+     *		},
+     *		{
+     *			"admin": "testadmin",
+     *			"date": "2016-03-21T17:50:09.258+0100",
+     *			"target": "marie",
+     *			"type": "Email sent"
+     *		}
+     *	]}
+     * 
+ * + */ + @RequestMapping(value = REQUEST_MAPPING + + "/{target}/{limit}/{page}", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + public List find(@PathVariable String target, @PathVariable int limit, @PathVariable int page) { + + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + // Filter logs by orgs users if user is not SUPERUSER + if (!auth.getAuthorities().contains(ROLE_SUPERUSER)) { + List users = new ArrayList(); + DelegationEntry delegation = this.delegationDao.findOne(auth.getName()); + String[] orgs = delegation.getOrgs(); + for (String org : orgs) + users.addAll(this.orgsDao.findByCommonName(org).getMembers()); + if (!users.contains(target)) + throw new AccessDeniedException("User not under delegation"); + } + + return this.logDao.findByTarget(target, new PageRequest(page, limit, new Sort(Sort.Direction.DESC, "date"))); + } + + @RequestMapping(value = REQUEST_MAPPING + + "/{limit}/{page}", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + public List find(HttpServletRequest request, @PathVariable int limit, @PathVariable int page) { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + // Filter logs by orgs users if user is not SUPERUSER + if (!auth.getAuthorities().contains(ROLE_SUPERUSER)) { + Set users = this.advancedDelegationDao.findUsersUnderDelegation(auth.getName()); + return this.logDao.myFindByTargets(users, + new PageRequest(page, limit, new Sort(Sort.Direction.DESC, "date"))); + } else { + return this.logDao.findAll(new PageRequest(page, limit, new Sort(Sort.Direction.DESC, "date"))) + .getContent(); + } + + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/orgs/OrgsController.java b/console/src/main/java/org/georchestra/console/ws/backoffice/orgs/OrgsController.java new file mode 100644 index 0000000000..b975f02fe6 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/orgs/OrgsController.java @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.orgs; + +import static org.georchestra.commons.security.SecurityHeaders.SEC_USERNAME; + +import java.io.IOException; +import java.io.PrintWriter; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.commons.security.SecurityHeaders; +import org.georchestra.console.dao.AdvancedDelegationDao; +import org.georchestra.console.dao.DelegationDao; +import org.georchestra.console.model.AdminLogType; +import org.georchestra.console.model.DelegationEntry; +import org.georchestra.console.ws.backoffice.utils.ResponseUtil; +import org.georchestra.console.ws.utils.LogUtils; +import org.georchestra.console.ws.utils.Validation; +import org.georchestra.ds.orgs.Org; +import org.georchestra.ds.orgs.OrgsDao; +import org.georchestra.lib.file.FileUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.access.prepost.PostFilter; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.google.common.collect.Sets; + +@Controller +public class OrgsController { + + private static final Log LOG = LogFactory.getLog(OrgsController.class.getName()); + + private static final String BASE_MAPPING = "/private"; + private static final String BASE_RESOURCE = "orgs"; + private static final String REQUEST_MAPPING = BASE_MAPPING + "/" + BASE_RESOURCE; + private static final String PUBLIC_REQUEST_MAPPING = "/public/" + BASE_RESOURCE; + + private static GrantedAuthority ROLE_SUPERUSER = new SimpleGrantedAuthority("ROLE_SUPERUSER"); + + @Autowired + private OrgsDao orgDao; + + @Autowired + protected Validation validation; + + @Autowired + protected DelegationDao delegationDao; + + @Autowired + protected AdvancedDelegationDao advancedDelegationDao; + + @Autowired + protected LogUtils logUtils; + + /** + * Areas map configuration + * + * This map appears on the /console/account/new page, when the user checks the + * "my org does not exist" checkbox. Currently the map is configured with the + * EPSG:4326 SRS. + */ + + /* Center of map */ + @Value("${AreaMapCenter:1.77, 47.3}") + private String areaMapCenter; + + /* Zoom of map */ + @Value("${AreaMapZoom:6}") + private String areaMapZoom; + + /* The following properties are used to configure the map widget behavior */ + + /* Key stored in the org LDAP record to uniquely identify a feature. */ + @Value("${AreasKey:INSEE_COM}") + private String areasKey; + + /* Feature "nice name" which appears in the widget list once selected. */ + @Value("${AreasValue:NOM_COM}") + private String areasValue; + + /* + * Feature property which is used to group together areas. + * + * eg: if the GeoJSON file represents regions, then AreasGroup might be the + * property with the "state name". + */ + @Value("${AreasGroup:NOM_DEP}") + private String areasGroup; + + @Autowired + public OrgsController(OrgsDao dao) { + this.orgDao = dao; + } + + /** + * Return a list of available organization as json array + */ + @RequestMapping(value = REQUEST_MAPPING, method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @PostFilter("hasPermission(filterObject, 'read')") + @ResponseBody + public List findAll() { + List orgs = this.orgDao.findAll(); + Collections.sort(orgs); + return orgs; + } + + /** + * Retreive full information about one org as JSON document. Following keys will + * be available : + * + * * 'id' (not used) * 'name' * 'shortName' * 'cities' as json array ex: + * [654,865498,98364,9834534,984984,6978984,98498] * 'status' * 'orgType' * + * 'address' * 'members' as json array ex: ["testadmin", "testuser"] + * + */ + @RequestMapping(value = REQUEST_MAPPING + + "/{cn:.+}", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + public Org getOrgInfos(@PathVariable String cn) { + this.checkOrgAuthorization(cn); + + return this.orgDao.findByCommonName(cn); + } + + /** + * Update information of one org + * + * Request should contain Json formated document containing following keys : + * + * * 'name' * 'shortName' * 'cities' as json array ex: + * [654,865498,98364,9834534,984984,6978984,98498] * 'status' * 'orgType' * + * 'address' * 'members' as json array ex: ["testadmin", "testuser"] + * + * All fields are optional. + * + * Full json example : { "name" : "Office national des forêts", "shortName" : + * "ONF", "cities" : [ 654, 865498, 98364, 9834534, 984984, 6978984, 98498 ], + * "status" : "inscrit", "type" : "association", "address" : "128 rue de la + * plante, 73059 Chambrille", "members": [ "testadmin", "testuser" ] } + * + */ + @RequestMapping(value = REQUEST_MAPPING + + "/{commonName:.+}", method = RequestMethod.PUT, produces = "application/json; charset=utf-8") + @ResponseBody + public Org updateOrgInfos(@PathVariable String commonName, HttpServletRequest request) + throws IOException, JSONException, SQLException { + + this.checkOrgAuthorization(commonName); + + // Parse Json + JSONObject json = this.parseRequest(request); + + // Validate request against required fields for admin part + if (!this.validation.validateOrgField("name", json)) { + throw new IOException("required field : name"); + } + + if (!this.validation.validateUrl(json.optString(Org.JSON_URL))) { + throw new IOException(String.format("bad org url format: %s", json.optString(Org.JSON_URL))); + } + + // Retrieve current orgs state from ldap + Org org = this.orgDao.findByCommonName(commonName); + final Org initialOrg = org.clone(); + + // get default pending status + Boolean defaultPending = org.isPending(); + + // Update org and orgExt fields + this.updateFromRequest(org, json); + + // Persist changes to LDAP server + this.orgDao.update(org); + + if (!commonName.equals(org.getId())) { + for (DelegationEntry delegation : this.advancedDelegationDao.findByOrg(commonName)) { + delegation.removeOrg(commonName); + delegation.setOrgs(ArrayUtils.add(delegation.getOrgs(), org.getId())); + this.delegationDao.save(delegation); + } + } + + // log org and orgExt changes + logUtils.logOrgChanged(initialOrg, json); + + // log if pending status change + String username = SecurityHeaders.decode(request.getHeader(SEC_USERNAME)); + if (username != null && defaultPending != org.isPending()) { + logUtils.createLog(org.getId(), AdminLogType.PENDING_ORG_ACCEPTED, null); + } + return org; + } + + /** + * Create a new org based on JSON document sent by browser. JSON document may + * contain following keys : + * + * * 'name' * 'shortName' (mandatory) * 'cities' as json array ex: + * [654,865498,98364,9834534,984984,6978984,98498] * 'status' * 'type' * + * 'address' * 'members' as json array ex: ["testadmin", "testuser"] + * + * All fields are optional except 'shortName' which is used to generate + * organization identifier. + * + * A new JSON document will be return to browser with a complete description of + * created org. @see updateOrgInfos() for JSON format. + */ + @RequestMapping(value = REQUEST_MAPPING, method = RequestMethod.POST, produces = "application/json; charset=utf-8") + @ResponseBody + @PreAuthorize("hasRole('SUPERUSER')") + public Org createOrg(HttpServletRequest request) throws IOException, JSONException { + // Parse Json + JSONObject json = this.parseRequest(request); + + // Validate request against required fields for admin part + if (!this.validation.validateOrgField("shortName", json)) { + throw new IOException("required field : shortName"); + } + + if (!this.validation.validateUrl(json.optString(Org.JSON_URL))) { + throw new IOException(String.format("bad org url format: %s", json.optString(Org.JSON_URL))); + } + + Org org = new Org(); + org.setId(""); + this.updateFromRequest(org, json); + + // Persist changes to LDAP server + this.orgDao.insert(org); + + logUtils.createLog(org.getId(), AdminLogType.ORG_CREATED, null); + + return org; + } + + /** + * Delete one org + */ + @RequestMapping(value = REQUEST_MAPPING + "/{commonName:.+}", method = RequestMethod.DELETE) + @PreAuthorize("hasRole('SUPERUSER')") + public void deleteOrg(@PathVariable String commonName, HttpServletResponse response) + throws IOException, SQLException { + + // Check if this role is part of a delegation + for (DelegationEntry delegation : this.advancedDelegationDao.findByOrg(commonName)) { + delegation.removeOrg(commonName); + this.delegationDao.save(delegation); + } + + // delete entities in LDAP server + Org org = this.orgDao.findByCommonName(commonName); + Boolean isPending = org.isPending(); + + this.orgDao.delete(org); + + // get authent info without request + if (isPending != null && isPending) { + logUtils.createLog(commonName, AdminLogType.PENDING_ORG_REFUSED, null); + } else { + logUtils.createLog(commonName, AdminLogType.ORG_DELETED, null); + } + + ResponseUtil.writeSuccess(response); + } + + @RequestMapping(value = PUBLIC_REQUEST_MAPPING + "/requiredFields", method = RequestMethod.GET) + public void getRequiredFieldsForOrgCreation(HttpServletResponse response) throws IOException, JSONException { + JSONArray fields = new JSONArray(); + validation.getRequiredOrgFields().forEach(fields::put); + ResponseUtil.buildResponse(response, fields.toString(4), HttpServletResponse.SC_OK); + } + + @RequestMapping(value = PUBLIC_REQUEST_MAPPING + "/orgTypeValues", method = RequestMethod.GET) + public void getOrganisationTypePossibleValues(HttpServletResponse response) throws IOException, JSONException { + JSONArray fields = new JSONArray(); + for (String field : this.orgDao.getOrgTypeValues()) { + fields.put(field); + } + ResponseUtil.buildResponse(response, fields.toString(4), HttpServletResponse.SC_OK); + } + + /** + * Return configuration areas UI as json object. Configuration includes + * following values : + * + * - inital map center - initial map zoom - ows service to retrieve area + * geometry 'url' - attribute to use as label 'value' - attribute to use to + * group area 'group' - attribute to use as identifier 'key' + * + * Ex : { "map" : { "center": [49.5468, 5.123486], "zoom": 8}, "areas" : { + * "url": "http://sdi.georchestra.org/geoserver/....;", "key": "insee_code", + * "value": "commune_name", "group": "department_name"} } + */ + + @RequestMapping(value = PUBLIC_REQUEST_MAPPING + "/areaConfig.json", method = RequestMethod.GET) + public void getAreaConfig(HttpServletResponse response) throws IOException, JSONException { + JSONObject res = new JSONObject(); + JSONObject map = new JSONObject(); + // Parse center + try { + String[] rawCenter = areaMapCenter.split("\\s*,\\s*"); + JSONArray center = new JSONArray(); + center.put(Double.parseDouble(rawCenter[0])); + center.put(Double.parseDouble(rawCenter[1])); + map.put("center", center); + map.put("zoom", areaMapZoom); + res.put("map", map); + } catch (Exception e) { + LOG.info("Could not parse value", e); + } + JSONObject areas = new JSONObject(); + areas.put("key", areasKey); + areas.put("value", areasValue); + areas.put("group", areasGroup); + res.put("areas", areas); + ResponseUtil.buildResponse(response, res.toString(4), HttpServletResponse.SC_OK); + } + + /** + * Return distribution of orgs by org type as json array or CSV + * + * json array contains objects with following keys: - type : org type - count : + * count of org with specified type + * + * CSV contains two columns one for org type and second for count + * + */ + @RequestMapping(value = BASE_MAPPING + "/orgsTypeDistribution.{format:(?:csv|json)}", method = RequestMethod.GET) + public void orgTypeDistribution(HttpServletResponse response, @PathVariable String format) + throws IOException, JSONException { + + Map distribution = new HashMap<>(); + List orgs = this.orgDao.findAll(); + + // Filter results if user is not SUPERUSER + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (!auth.getAuthorities().contains(ROLE_SUPERUSER)) { + String[] filteredIds = this.delegationDao.findOne(auth.getName()).getOrgs(); + if (filteredIds != null && filteredIds.length > 0) { + final Set retain = Sets.newHashSet(filteredIds); + orgs = orgs.stream().filter(o -> retain.contains(o.getName())).collect(Collectors.toList()); + } + } + + for (Org org : orgs) { + try { + distribution.put(org.getOrgType(), distribution.get(org.getOrgType()) + 1); + } catch (NullPointerException e) { + distribution.put(org.getOrgType(), 1); + } + } + + PrintWriter out = response.getWriter(); + + if (format.equalsIgnoreCase("csv")) { + response.setContentType("text/csv"); + response.setHeader("Content-Disposition", "attachment;filename=orgsTypeDistribution.csv"); + + out.println("organisation type, count"); + for (String type : distribution.keySet()) { + out.println(type + "," + distribution.get(type)); + } + out.close(); + + } else if (format.equalsIgnoreCase("json")) { + response.setContentType("application/json"); + + JSONArray res = new JSONArray(); + for (String type : distribution.keySet()) { + res.put(new JSONObject().put("type", type).put("count", distribution.get(type))); + } + out.println(res.toString(4)); + out.close(); + } + + } + + /** + * Check authorization on org. Throw an exception if current user does not have + * rights to edit or delete specified org. + * + * @param org Org identifier + * @throws AccessDeniedException if current user does not have permissions to + * edit this org + */ + private void checkOrgAuthorization(String org) throws AccessDeniedException { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + // Verify authent context and that org is under delegation if user is not + // SUPERUSER + if (auth != null && auth.getName() != null && !auth.getAuthorities().contains(ROLE_SUPERUSER)) { + DelegationEntry delegation = this.delegationDao.findOne(auth.getName()); + if (delegation != null) { + if (!Arrays.asList(delegation.getOrgs()).contains(org)) { + throw new AccessDeniedException("Org not under delegation"); + } + } else { + throw new AccessDeniedException("Org not under delegation"); + } + } + } + + /** + * Update org instance based on field found in json object. + * + * All field of Org instance will be updated if corresponding key exists in json + * document except 'members'. + * + * @param org Org instance to update + * @param json Json document to take information from + * @throws JSONException If something went wrong during information extraction + * from json document + */ + protected void updateFromRequest(Org org, JSONObject json) throws IOException { + org.setId(orgDao.reGenerateId(json.optString(Org.JSON_SHORT_NAME), org.getId())); + org.setName(json.optString(Org.JSON_NAME)); + org.setShortName(json.optString(Org.JSON_SHORT_NAME)); + if (!json.isNull(Org.JSON_CITIES)) { + List cities = new ArrayList<>(); + if (!json.getJSONArray(Org.JSON_CITIES).isEmpty()) { + cities = StreamSupport.stream(json.getJSONArray(Org.JSON_CITIES).spliterator(), false) + .map(Object::toString).collect(Collectors.toList()); + } + org.setCities(cities); + } + if (!json.isNull(Org.JSON_MEMBERS)) { + org.setMembers(StreamSupport.stream(json.getJSONArray(Org.JSON_MEMBERS).spliterator(), false) + .map(Object::toString).collect(Collectors.toList())); + } + org.setPending(json.optBoolean(Org.JSON_PENDING)); + + org.setOrgType(json.optString(Org.JSON_ORG_TYPE)); + org.setAddress(json.optString(Org.JSON_ADDRESS)); + org.setDescription(json.optString(Org.JSON_DESCRIPTION)); + org.setNote(json.optString(Org.JSON_NOTE)); + org.setUrl(json.optString(Org.JSON_URL)); + org.setLogo(json.optString(Org.JSON_LOGO)); + } + + /** + * Parse request payload and return a JSON document + * + * @param request + * @return JSON document corresponding to browser request + * @throws IOException if error occurs during JSON parsing + */ + private JSONObject parseRequest(HttpServletRequest request) throws IOException, JSONException { + return new JSONObject(FileUtils.asString(request.getInputStream())); + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/platform/InfosController.java b/console/src/main/java/org/georchestra/console/ws/backoffice/platform/InfosController.java new file mode 100644 index 0000000000..a296a341b7 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/platform/InfosController.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.platform; + +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class InfosController { + /** + * Bring general information about georchestra or console. + */ + private static final String BASE_MAPPING = "/private"; + + @Value("${saslEnabled:false}") + private boolean saslEnabled; + + @Value("${saslServer:#{null}}") + private String saslServer; + + @GetMapping(value = BASE_MAPPING + "/platform/infos", produces = "application/json; charset=utf-8") + @PreAuthorize(value = "hasRole('SUPERUSER')") + @ResponseBody + public String getPlatformInfos() { + JSONObject ret = new JSONObject(); + ret.put("saslEnabled", saslEnabled); + ret.put("saslServer", saslServer); + return ret.toString(); + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/roles/RoleListResponse.java b/console/src/main/java/org/georchestra/console/ws/backoffice/roles/RoleListResponse.java new file mode 100644 index 0000000000..d9f9bb8ac4 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/roles/RoleListResponse.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.roles; + +import java.util.List; + +import org.georchestra.ds.roles.Role; +import org.georchestra.ds.roles.RoleSchema; +import org.georchestra.ds.users.ProtectedUserFilter; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Returns the list of users / roles membership. + * + * @author Mauricio Pazos + * + */ + +final class RoleListResponse { + + private List roleList; + private ProtectedUserFilter filter; + + public RoleListResponse(List list, ProtectedUserFilter filter) { + this.roleList = list; + this.filter = filter; + } + + public JSONArray toJsonArray() throws JSONException { + JSONArray jsonRoleArray = new JSONArray(); + for (Role role : this.roleList) { + + JSONObject jsonRole = new JSONObject(); + + jsonRole.put(RoleSchema.COMMON_NAME_KEY, role.getName()); + jsonRole.put(RoleSchema.DESCRIPTION_KEY, role.getDescription()); + jsonRole.put(RoleSchema.FAVORITE_JSON_KEY, role.isFavorite()); + + // adds the list of users + List list = filter.filterStringList(role.getUserList()); + + JSONArray membersArray = new JSONArray(); + + for (String userUid : list) + membersArray.put(userUid); + + jsonRole.put("users", membersArray); + + jsonRoleArray.put(jsonRole); + } + return jsonRoleArray; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/roles/RoleResponse.java b/console/src/main/java/org/georchestra/console/ws/backoffice/roles/RoleResponse.java new file mode 100644 index 0000000000..8b9b55f429 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/roles/RoleResponse.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.roles; + +import java.io.IOException; +import java.util.List; + +import org.georchestra.ds.roles.Role; +import org.georchestra.ds.roles.RoleSchema; +import org.georchestra.ds.users.ProtectedUserFilter; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * @author Mauricio Pazos + * + */ +public class RoleResponse { + + private Role role; + private ProtectedUserFilter filter; + + public RoleResponse(Role role, ProtectedUserFilter filter) { + + this.role = role; + this.filter = filter; + } + + public String asJsonString() throws IOException { + try { + JSONObject jsonRole = new JSONObject(); + + jsonRole.put(RoleSchema.COMMON_NAME_KEY, this.role.getName()); + jsonRole.put(RoleSchema.DESCRIPTION_KEY, this.role.getDescription()); + jsonRole.put(RoleSchema.FAVORITE_JSON_KEY, this.role.isFavorite()); + + // adds the list of users + List list = filter.filterStringList(this.role.getUserList()); + + JSONArray membersArray = new JSONArray(); + int j = 0; + for (String userUid : list) { + + membersArray.put(j, userUid); + j++; + } + jsonRole.put("users", membersArray); + + return jsonRole.toString(); + + } catch (JSONException ex) { + + throw new IOException(ex); + } + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/roles/RolesController.java b/console/src/main/java/org/georchestra/console/ws/backoffice/roles/RolesController.java new file mode 100644 index 0000000000..81eb927b4a --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/roles/RolesController.java @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.roles; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.console.dao.AdvancedDelegationDao; +import org.georchestra.console.dao.DelegationDao; +import org.georchestra.console.model.AdminLogType; +import org.georchestra.console.model.DelegationEntry; +import org.georchestra.console.ws.backoffice.utils.RequestUtil; +import org.georchestra.console.ws.backoffice.utils.ResponseUtil; +import org.georchestra.console.ws.utils.LogUtils; +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.DuplicatedCommonNameException; +import org.georchestra.ds.roles.Role; +import org.georchestra.ds.roles.RoleDao; +import org.georchestra.ds.roles.RoleFactory; +import org.georchestra.ds.roles.RoleSchema; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.AccountDao; +import org.georchestra.ds.users.ProtectedUserFilter; +import org.georchestra.ds.users.UserRule; +import org.georchestra.lib.file.FileUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.access.prepost.PostFilter; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * Web Services to maintain the Roles information. + * + * @author Mauricio Pazos + * + */ + +@Controller +public class RolesController { + + private static final Log LOG = LogFactory.getLog(RolesController.class.getName()); + + public static final GrantedAuthority ROLE_SUPERUSER = new SimpleGrantedAuthority("ROLE_SUPERUSER"); + + private static final String BASE_MAPPING = "/private"; + private static final String BASE_RESOURCE = "roles"; + private static final String REQUEST_MAPPING = BASE_MAPPING + "/" + BASE_RESOURCE; + + private static final String DUPLICATED_COMMON_NAME = "duplicated_common_name"; + private static final String NOT_FOUND = "not_found"; + private static final String USER_NOT_FOUND = "user_not_found"; + private static final String ILLEGAL_CHARACTER = "illegal_character"; + + private static final String VIRTUAL_TEMPORARY_ROLE_NAME = "TEMPORARY"; + private static final String VIRTUAL_TEMPORARY_ROLE_DESCRIPTION = "Virtual role that contains all temporary users"; + + private static final String VIRTUAL_EXPIRED_ROLE_NAME = "EXPIRED"; + private static final String VIRTUAL_EXPIRED_ROLE_DESCRIPTION = "Virtual role that contains all expired users"; + + @Autowired + private AccountDao accountDao; + + @Autowired + protected LogUtils logUtils; + + @Autowired + private AdvancedDelegationDao advancedDelegationDao; + + @Autowired + private DelegationDao delegationDao; + + private RoleDao roleDao; + private ProtectedUserFilter filter; + + /** + * Builds a JSON response in case of error. + * + * @param mesg a descriptive message of the encountered error. + * @return a string of the response. + * + * TODO: This code sounds pretty similar to what is done in + * ResponseUtil.java:buildResponseMessage() and might deserve a + * refactor. + */ + + private String buildErrorResponse(String mesg) { + HashMap map = new HashMap(); + map.put("success", false); + map.put("error_message", mesg); + return new JSONObject(map).toString(); + } + + @Autowired + public RolesController(RoleDao dao, UserRule userRule) { + this.roleDao = dao; + this.filter = new ProtectedUserFilter(userRule.getListUidProtected()); + } + + /** + * Returns all roles. Each roles will contains its list of users. + * + * @throws IOException + */ + @RequestMapping(value = REQUEST_MAPPING, method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @PostFilter("hasPermission(filterObject, 'read')") + @ResponseBody + public List findAll() throws DataServiceException { + List list = this.roleDao.findAll(); + list.stream().forEach(role -> { + role.setUserList(filter.filterStringList(role.getUserList())); + }); + Pair virtualRoles = this.generateVirtualRoles(); + list.addAll(Arrays.asList(virtualRoles.getLeft(), virtualRoles.getRight())); + return list; + } + + /** + * Returns the detailed information of the role, with its list of users. + * + *

+ * If the role identifier is not present in the ldap store an + * {@link IOException} will be throw. + *

+ *

+ * URL Format: [BASE_MAPPING]/roles/{cn} + *

+ *

+ * Example: [BASE_MAPPING]/roles/role44 + *

+ * + * @param cn Comon name of role + * @throws IOException + */ + @RequestMapping(value = REQUEST_MAPPING + + "/{cn:.+}", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + public Role findByCN(@PathVariable String cn) throws DataServiceException { + if (StringUtils.isEmpty(cn)) { + throw new IllegalArgumentException("name is empty"); + } + Role res; + if (cn.equals(RolesController.VIRTUAL_TEMPORARY_ROLE_NAME)) + res = this.generateVirtualRoles().getLeft(); + else + res = this.roleDao.findByCommonName(cn); + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (!auth.getAuthorities().contains(ROLE_SUPERUSER)) { + if (!Arrays.asList(this.delegationDao.findOne(auth.getName()).getRoles()).contains(cn)) + throw new AccessDeniedException("Role not under delegation"); + res.getUserList().retainAll(this.advancedDelegationDao.findUsersUnderDelegation(auth.getName())); + } + return res; + } + + private Pair generateVirtualRoles() { + + Role tempRole = RoleFactory.create(RolesController.VIRTUAL_TEMPORARY_ROLE_NAME, + RolesController.VIRTUAL_TEMPORARY_ROLE_DESCRIPTION, false); + Role expiredRole = RoleFactory.create(RolesController.VIRTUAL_EXPIRED_ROLE_NAME, + RolesController.VIRTUAL_EXPIRED_ROLE_DESCRIPTION, false); + + Date today = Calendar.getInstance().getTime(); + + this.accountDao.findByShadowExpire().stream().forEach(it -> { + if (it.getShadowExpire() != null && today.after(it.getShadowExpire())) { + expiredRole.addUser(it.getUid()); + } + tempRole.addUser(it.getUid()); + }); + return Pair.of(tempRole, expiredRole); + } + + /** + * + *

+ * Creates a new role. + *

+ * + *
+     * Request
+     *
+     * role data:
+     * {
+     *   "cn": "Name of the role"
+     *   "description": "Description for the role"
+     *   }
+     * 
+ * + *
+     * Response
+     *
+     * - Success case
+     *
+     * {
+     *  "cn": "Name of the role",
+     *  "description": "Description for the role"
+     * }
+     * 
+ * + * @param request + * @param response + * @throws IOException + */ + @RequestMapping(value = REQUEST_MAPPING, method = RequestMethod.POST) + @PreAuthorize("hasRole('SUPERUSER')") + public void create(HttpServletRequest request, HttpServletResponse response) throws IOException { + + try { + Role role = createRoleFromRequestBody(request.getInputStream()); + + this.roleDao.insert(role); + RoleResponse roleResponse = new RoleResponse(role, this.filter); + String jsonResponse = roleResponse.asJsonString(); + ResponseUtil.buildResponse(response, jsonResponse, HttpServletResponse.SC_OK); + + // log role creation + logUtils.createLog(role.getName(), AdminLogType.ROLE_CREATED, null); + + } catch (DuplicatedCommonNameException emailex) { + + String jsonResponse = ResponseUtil.buildResponseMessage(Boolean.FALSE, DUPLICATED_COMMON_NAME); + + ResponseUtil.buildResponse(response, jsonResponse, HttpServletResponse.SC_CONFLICT); + + } catch (DataServiceException dsex) { + LOG.error(dsex.getMessage()); + ResponseUtil.buildResponse(response, buildErrorResponse(dsex.getMessage()), + HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + throw new IOException(dsex); + } catch (IllegalArgumentException ex) { + String jsonResponse = ResponseUtil.buildResponseMessage(Boolean.FALSE, ILLEGAL_CHARACTER); + ResponseUtil.buildResponse(response, jsonResponse, HttpServletResponse.SC_CONFLICT); + } + } + + /** + * Deletes the role. + * + * The request format is: + * + *
+     * [BASE_MAPPING]/roles/{cn}
+     *
+     * Where cn is the name of role to delete.
+     * 
+ * + * @param response + * @param cn Common name of role to delete + * @throws IOException + */ + @RequestMapping(value = REQUEST_MAPPING + "/{cn:.+}", method = RequestMethod.DELETE) + @PreAuthorize("hasRole('SUPERUSER')") + public void delete(HttpServletResponse response, @PathVariable String cn) throws IOException { + try { + + // Check if this role is part of a delegation + for (DelegationEntry delegation : this.advancedDelegationDao.findByRole(cn)) { + delegation.removeRole(cn); + this.delegationDao.save(delegation); + } + + this.roleDao.delete(cn); + + // log role deleted + logUtils.createLog(cn, AdminLogType.ROLE_DELETED, null); + + ResponseUtil.writeSuccess(response); + + } catch (NameNotFoundException e) { + LOG.error(e.getMessage()); + ResponseUtil.buildResponse(response, buildErrorResponse(e.getMessage()), HttpServletResponse.SC_NOT_FOUND); + } catch (DataServiceException e) { + LOG.error(e.getMessage()); + ResponseUtil.buildResponse(response, buildErrorResponse(e.getMessage()), + HttpServletResponse.SC_BAD_REQUEST); + } catch (Exception e) { + LOG.error(e.getMessage()); + ResponseUtil.buildResponse(response, buildErrorResponse(e.getMessage()), + HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + throw new IOException(e); + } + } + + /** + * Modifies the role using the fields provided in the request body. + *

+ * The fields that are not present in the parameters will remain untouched in + * the LDAP store. + *

+ * + *
+     * The request format is:
+     * [BASE_MAPPING]/roles/{cn}
+     *
+     * Where cn is the name of role to update.
+     * 
+ *

+ * The request body should contains a the fields to modify using the JSON + * syntax. + *

+ *

+ * Example: + *

+ * + *
+     * Request
+     * [BASE_MAPPING]/roles/users
+     *
+     * Body request: 
+     * role data:
+     * {
+     *   "cn": "newName"
+     *   "description": "new Description"
+     *   }
+     *
+     * 
+ * + * @param request [BASE_MAPPING]/roles/{cn} body request {"cn": value1, + * "description": value2 } + * @param response + * + * @throws IOException if the uid does not exist or fails to access to the LDAP + * store. + */ + @RequestMapping(value = REQUEST_MAPPING + "/{cn:.+}", method = RequestMethod.PUT) + @PreAuthorize("hasRole('SUPERUSER')") + public void update(HttpServletRequest request, HttpServletResponse response, @PathVariable String cn) + throws IOException { + + // searches the role + Role role; + try { + role = this.roleDao.findByCommonName(cn); + } catch (NameNotFoundException e) { + ResponseUtil.writeError(response, NOT_FOUND); + return; + } catch (DataServiceException e) { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + throw new IOException(e); + } + + // modifies the role data + try { + final Role modified = modifyRole(role, request.getInputStream()); + + this.roleDao.update(cn, modified); + + RoleResponse roleResponse = new RoleResponse(role, this.filter); + + String jsonResponse = roleResponse.asJsonString(); + + ResponseUtil.buildResponse(response, jsonResponse, HttpServletResponse.SC_OK); + + ResponseUtil.writeSuccess(response); + + } catch (NameNotFoundException e) { + + ResponseUtil.buildResponse(response, ResponseUtil.buildResponseMessage(Boolean.FALSE, NOT_FOUND), + HttpServletResponse.SC_NOT_FOUND); + + return; + + } catch (DuplicatedCommonNameException e) { + + String jsonResponse = ResponseUtil.buildResponseMessage(Boolean.FALSE, DUPLICATED_COMMON_NAME); + + ResponseUtil.buildResponse(response, jsonResponse, HttpServletResponse.SC_CONFLICT); + + return; + + } catch (DataServiceException e) { + LOG.error(e.getMessage()); + ResponseUtil.buildResponse(response, buildErrorResponse(e.getMessage()), + HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + throw new IOException(e); + } + } + + /** + * Updates the users of role. This method will add or delete the role of users + * from the list of roles. + * + * @param request request [BASE_MAPPING]/roles_users body request {"users": + * [u1,u2,u3], "PUT": [g1,g2], "DELETE":[g3,g4] } + * @param response + * @throws IOException + */ + @RequestMapping(value = BASE_MAPPING + "/roles_users", method = RequestMethod.POST) + public void updateUsers(HttpServletRequest request, HttpServletResponse response) + throws AccessDeniedException, IOException, JSONException, DataServiceException { + + JSONObject json = new JSONObject(FileUtils.asString(request.getInputStream())); + + List users = createUserList(json, "users"); + List putRole = createUserList(json, "PUT"); + List deleteRole = createUserList(json, "DELETE"); + + List accounts = users.stream().map(userUuid -> { + try { + return accountDao.findByUID(userUuid); + } catch (DataServiceException e) { + LOG.debug(e.getMessage()); + return null; + } + }).filter(account -> null != account).collect(Collectors.toList()); + + // Don't allow modification of ORGADMIN role + if (putRole.contains("ORGADMIN") || deleteRole.contains("ORGADMIN")) { + throw new IllegalArgumentException("ORGADMIN role cannot be add or delete"); + } + + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (!auth.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_SUPERUSER"))) { + this.checkAuthorization(auth.getName(), users, putRole, deleteRole); + } + + this.roleDao.addUsersInRoles(putRole, accounts); + this.roleDao.deleteUsersInRoles(deleteRole, accounts); + + // create log + logUtils.logRolesUsersAction(putRole, deleteRole, accounts); + + ResponseUtil.writeSuccess(response); + } + + public void checkAuthorization(String delegatedAdmin, List users, List putRole, + List deleteRole) throws AccessDeniedException { + // Verify authorization + Set usersUnderDelegation = this.advancedDelegationDao.findUsersUnderDelegation(delegatedAdmin); + if (!usersUnderDelegation.containsAll(users)) + throw new AccessDeniedException("Some users are not under delegation"); + DelegationEntry delegation = this.delegationDao.findOne(delegatedAdmin); + if (!Arrays.asList(delegation.getRoles()).containsAll(putRole)) + throw new AccessDeniedException("Some roles are not under delegation (put)"); + if (!Arrays.asList(delegation.getRoles()).containsAll(deleteRole)) + throw new AccessDeniedException("Some roles are not under delegation (delete)"); + + } + + private List createUserList(JSONObject json, String arrayKey) throws IOException { + + try { + + List list = new LinkedList(); + + JSONArray jsonArray = json.getJSONArray(arrayKey); + for (int i = 0; i < jsonArray.length(); i++) { + + list.add(jsonArray.getString(i)); + } + + return list; + + } catch (Exception e) { + LOG.error(e.getMessage()); + throw new IOException(e); + } + } + + /** + * Modifies the original field using the values in the inputStream. + * + * @param role role to modify + * @param inputStream contains the new values + * + * @return the {@link Role} modified + */ + private Role modifyRole(Role role, ServletInputStream inputStream) throws IOException { + + String strRole = FileUtils.asString(inputStream); + JSONObject json; + try { + json = new JSONObject(strRole); + } catch (JSONException e) { + LOG.error(e.getMessage()); + throw new IOException(e); + } + + String cn = RequestUtil.getFieldValue(json, RoleSchema.COMMON_NAME_KEY); + if (cn != null) { + role.setName(cn); + } + + String description = RequestUtil.getFieldValue(json, RoleSchema.DESCRIPTION_KEY); + if (description != null) { + role.setDescription(description); + } + + Boolean isFavorite = RequestUtil.getBooleanFieldValue(json, RoleSchema.FAVORITE_JSON_KEY); + if (isFavorite != null) + role.setFavorite(isFavorite); + + return role; + } + + private Role createRoleFromRequestBody(ServletInputStream is) throws IOException, IllegalArgumentException { + try { + String strRole = FileUtils.asString(is); + JSONObject json = new JSONObject(strRole); + + String commonName = RequestUtil.getFieldValue(json, RoleSchema.COMMON_NAME_KEY); + if (commonName == null) { + throw new IllegalArgumentException(RoleSchema.COMMON_NAME_KEY + " is required"); + } + + // Capitalize role name and check format + commonName = commonName.toUpperCase(); + Pattern p = Pattern.compile("[A-Z0-9_-]+"); + if (!p.matcher(commonName).matches()) + throw new IllegalArgumentException(RoleSchema.COMMON_NAME_KEY + + " should only contain uppercased letters, digits, dashes and underscores"); + + String description = RequestUtil.getFieldValue(json, RoleSchema.DESCRIPTION_KEY); + Boolean isFavorite = RequestUtil.getBooleanFieldValue(json, RoleSchema.FAVORITE_JSON_KEY); + + Role role = RoleFactory.create(commonName, description, isFavorite); + + return role; + + } catch (IllegalArgumentException e) { + throw e; + } catch (Exception e) { + LOG.error(e.getMessage()); + throw new IOException(e); + } + } + + /** + * Method used for testing convenience. + * + * @param gd + */ + public void setRoleDao(RoleDao gd) { + roleDao = gd; + } + + /** + * Method used for testing convenience. + * + * @param ad + */ + public void setAccountDao(AccountDao ad) { + this.accountDao = ad; + } + + public AdvancedDelegationDao getAdvancedDelegationDao() { + return advancedDelegationDao; + } + + public void setAdvancedDelegationDao(AdvancedDelegationDao advancedDelegationDao) { + this.advancedDelegationDao = advancedDelegationDao; + } + + public DelegationDao getDelegationDao() { + return delegationDao; + } + + public void setDelegationDao(DelegationDao delegationDao) { + this.delegationDao = delegationDao; + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/users/CSVAccountExporter.java b/console/src/main/java/org/georchestra/console/ws/backoffice/users/CSVAccountExporter.java new file mode 100644 index 0000000000..dfa9402059 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/users/CSVAccountExporter.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2019 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ +package org.georchestra.console.ws.backoffice.users; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +import javax.annotation.Nullable; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.QuoteMode; +import org.georchestra.ds.orgs.Org; +import org.georchestra.ds.orgs.OrgsDao; +import org.georchestra.ds.users.Account; + +import lombok.Getter; +import lombok.NonNull; + +public class CSVAccountExporter { + + static enum OutlookCSVHeaderField { + FIRST_NAME("First Name", Account::getCommonName), // + MIDDLE_NAME("Middle Name"), // + LAST_NAME("Last Name", Account::getSurname), // + TITLE("Title", Account::getTitle), // + SUFFIX("Suffix"), // + INITIALS("Initials"), // + WEBPAGE("Web Page"), // + GENDER("Gender"), // + BDAY("Birthday"), // + ANNIVERSARY("Anniversary"), // + LOCATION("Location"), // + LANG("Language"), // + INTERNET_FREE_BUSY("Internet Free Busy"), // + NOTES("Notes", (a, o) -> o == null ? null : o.getDescription()), // + EMAIL("E-mail Address", Account::getEmail), // + EMAIL2("E-mail 2 Address"), // + EMAIL3("E-mail 3 Address"), // + PHONE("Primary Phone", Account::getPhone), // + HOME_PHONE("Home Phone"), // + HOME_PHONE2("Home Phone 2"), // + MOBILE_PHONE("Mobile Phone", Account::getMobile), // + PAGER("Pager"), // + HOME_FAX("Home Fax"), // + HOME_ADDRESS("Home Address", Account::getHomePostalAddress), // + HOME_STREET("Home Street"), // + HOME_STREET2("Home Street 2"), // + HOME_STREET3("Home Street 3"), // + HOME_POBOX("Home Address PO Box"), // + HOME_CITY("Home City", Account::getLocality), // + HOME_STATE("Home State"), // + HOME_PC("Home Postal Code"), // + HOME_COUNTRY("Home Country"), // + SPOUSE("Spouse"), // + CHILDREN("Children"), // + MANAGER_NAME("Manager's Name", Account::getManager), // + ASSISTANT_NAME("Assistant's Name"), // + REFERRED_BY("Referred By"), // + COMPANY_PHONE("Company Main Phone"), // + BIZ_PHONE("Business Phone"), // + BIZ_PHONE2("Business Phone 2"), // + BIZ_FAX("Business Fax", Account::getFacsimile), // + ASSISTANT_PHONE("Assistant's Phone"), // + COMPANY("Company", (a, org) -> org == null ? null : org.getName()), // + JOB_TITLE("Job Title", Account::getDescription), // + DEPARTMENT("Department"), // + OFFICE_LOCATION("Office Location"), // + ORG_ID("Organizational ID Number"), // + PROFESSION("Profession"), // + ACCOUNT("Account"), // + BIZ_ADDRESS("Business Address", Account::getPostalAddress), // + BIZ_ADDR_STREET("Business Street", Account::getStreet), // + BIZ_ADDR_STREET2("Business Street 2"), // + BIZ_ADDR_STREET3("Business Street 3"), // + BIZ_ADDR_POBOX("Business Address PO Box", Account::getPostOfficeBox), // + BIZ_CITY("Business City", (a, o) -> { + if (o == null || o.getCities() == null) { + return null; + } + return o.getCities().stream().collect(Collectors.joining(",")); + }), // + BIZ_STATE("Business State"), // + BIZ_PC("Business Postal Code", Account::getPostalCode), // + BIZ_COUNTRY("Business Country", Account::getStateOrProvince), // + OTHER_PHONE("Other Phone"), // + OTHER_FAX("Other Fax"), // + OTHER_ADDR("Other Address", Account::getRegisteredAddress), // + OTHER_ST("Other Street", Account::getPhysicalDeliveryOfficeName), // + OTHER_ST2("Other Street 2"), // + OTHER_ST3("Other Street 3"), // + OTHER_POBOX("Other Address PO Box"), // + OTHER_CITY("Other City"), // + OTHER_STATE("Other State"), // + OTHER_PC("Other Postal Code"), // + OTHER_COUNTRY("Other Country"), // + CALLBACK("Callback"), // + CAR_PHONE("Car Phone"), // + ISDN("ISDN"), // + RADIO_PHONE("Radio Phone"), // + TTY_PHONE("TTY/TDD Phone"), // + TELEX("Telex"), // + USER1("User 1"), // + USER2("User 2"), // + USER3("User 3"), // + USER4("User 4"), // + KEYWORDS("Keywords"), // + MILEAGE("Mileage"), // + HOBBY("Hobby"), // + BILLING_INFO("Billing Information"), // + DIRECTORY_SERVER("Directory Server"), // + SENSITIVITY("Sensitivity"), // + PRIORITY("Priority"), // + PRIVATE("Private", Account::getNote), // + CATEGORIES("Categories"); + + private final @NonNull @Getter String name; + private final BiFunction valueExtractor; + + private OutlookCSVHeaderField(String name) { + this.name = name; + this.valueExtractor = (a, o) -> null; + } + + private OutlookCSVHeaderField(@NonNull String name, @NonNull Function valueExtractor) { + this.name = name; + this.valueExtractor = (a, o) -> valueExtractor.apply(a); + } + + private OutlookCSVHeaderField(@NonNull String name, @NonNull BiFunction valueExtractor) { + this.name = name; + this.valueExtractor = valueExtractor; + } + + public String apply(Account acc, Org org) { + return this.valueExtractor.apply(acc, org); + } + } + + /** + * The commons-csv format used to generate the CSV exports, explicitly using + * {@link QuoteMode#MINIMAL MINIMAL} quote mode so the delimiters are added only + * if needed. + * + * @see CSVFormat#RFC4180 + */ + static final CSVFormat FORMAT = CSVFormat.RFC4180.withHeader(headerNames()).withQuoteMode(QuoteMode.MINIMAL); + + private final OrgsDao orgsDao; + + public CSVAccountExporter(@NonNull OrgsDao orgsDao) { + this.orgsDao = orgsDao; + } + + private static final String[] headerNames() { + return Arrays.stream(OutlookCSVHeaderField.values()).map(OutlookCSVHeaderField::getName).toArray(String[]::new); + } + + /** + * Exports a CSV file with Outlook compliant headers to the given target for the + * provided accounts + */ + public void export(@NonNull Iterable accounts, @NonNull Appendable target) throws IOException { + Map orgsById = new HashMap<>(); + + final CSVPrinter printer = FORMAT.print(target); + for (Account acc : accounts) { + Org org = orgsById.computeIfAbsent(acc.getOrg(), id -> orgsDao.findByUser(acc)); + printer.printRecord(toRecord(acc, org)); + } + printer.flush(); + } + + private Iterable toRecord(@NonNull Account account, @Nullable Org org) { + List values = new ArrayList<>(); + for (OutlookCSVHeaderField header : OutlookCSVHeaderField.values()) { + values.add(header.apply(account, org)); + } + return values; + } + + public static void main(String... args) { + Arrays.asList(OutlookCSVHeaderField.values()).forEach(f -> System.err.println(f.getName())); + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/users/DeletedUserDataInfo.java b/console/src/main/java/org/georchestra/console/ws/backoffice/users/DeletedUserDataInfo.java new file mode 100644 index 0000000000..dbd35ba46b --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/users/DeletedUserDataInfo.java @@ -0,0 +1,13 @@ +package org.georchestra.console.ws.backoffice.users; + +import lombok.Builder; +import lombok.Data; +import lombok.NonNull; + +public @Data @Builder class DeletedUserDataInfo { + private @NonNull String account; + private Integer metadata; + private Integer extractor; + private Integer geodocs; + private Integer ogcStats; +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/users/GDPRAccountWorker.java b/console/src/main/java/org/georchestra/console/ws/backoffice/users/GDPRAccountWorker.java new file mode 100644 index 0000000000..8f703a0579 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/users/GDPRAccountWorker.java @@ -0,0 +1,426 @@ +package org.georchestra.console.ws.backoffice.users; + +import static java.util.concurrent.CompletableFuture.runAsync; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import javax.annotation.Nullable; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.QuoteMode; +import org.apache.commons.io.FileUtils; +import org.georchestra.console.ds.AccountGDPRDao; +import org.georchestra.console.ds.AccountGDPRDao.DeletedRecords; +import org.georchestra.console.ds.AccountGDPRDao.ExtractorRecord; +import org.georchestra.console.ds.AccountGDPRDao.GeodocRecord; +import org.georchestra.console.ds.AccountGDPRDao.MetadataRecord; +import org.georchestra.console.ds.AccountGDPRDao.OgcStatisticsRecord; +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.AccountDao; +import org.locationtech.jts.io.WKTWriter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.ldap.NameNotFoundException; +import org.zeroturnaround.zip.ZipUtil; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Cleanup; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.Value; +import lombok.extern.slf4j.Slf4j; + +/** + * GDPR (EU General Data Protection + * Regulation) compliance data gatherer and mangler. + *

+ * For a given account, all it's GDPR sensitive data is gathered as a zip file + * bundle and returned as a {@link Resource}. + * + */ +@Slf4j +public class GDPRAccountWorker { + + private static final DateTimeFormatter FILENAME_DATE_FORMAT = DateTimeFormatter.ISO_LOCAL_DATE; + private static final DateTimeFormatter CONTENT_DATE_FORMAT = DateTimeFormatter.ISO_DATE_TIME; + + private @Autowired @Setter(AccessLevel.PACKAGE) AccountGDPRDao accountGDPRDao; + private @Autowired @Setter(AccessLevel.PACKAGE) UserInfoExporter userInfoExporter; + private @Autowired @Setter(AccessLevel.PACKAGE) AccountDao accountDao; + + public static @Value @Builder class DeletedAccountSummary { + private String accountId; + private int metadataRecords; + private int extractorRecords; + private int geodocsRecords; + private int ogcStatsRecords; + } + + public DeletedAccountSummary deleteAccountRecords(@NonNull Account account) throws DataServiceException { + log.info("GDPR: deleting or obfuscating all activity records related to the deleted account {}", + account.getUid()); + + DeletedRecords recs = accountGDPRDao.deleteAccountRecords(account); + DeletedAccountSummary summary = DeletedAccountSummary.builder()// + .accountId(recs.getAccountId())// + .metadataRecords(recs.getMetadataRecords())// + .extractorRecords(recs.getExtractorRecords())// + .geodocsRecords(recs.getGeodocsRecords())// + .ogcStatsRecords(recs.getOgcStatsRecords())// + .build(); + log.info("GDPR: deleted info summary: {}", summary); + return summary; + } + + /** + * Creates a ZIP file resource containing all downloadable data for the given + * user. + *

+ * Once the returned resource has been dealt with, {@link #dispose(Resource)} + * shall be called in order to clean up any temporary content it may be holding + * up. + * + * @throws DataServiceException + * @throws NameNotFoundException + * @throws IOException + */ + public Resource generateUserData(@NonNull String userId) + throws NameNotFoundException, DataServiceException, IOException { + Account account = accountDao.findByUID(userId); + return generateUserData(account); + } + + @VisibleForTesting + Resource generateUserData(@NonNull Account account) throws IOException { + try { + Path zipFile = buildCompressedUserDataResource(account); + return new FileSystemResource(zipFile.toFile()); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + Throwables.throwIfUnchecked(e); + Throwables.propagateIfPossible(e, IOException.class); + throw new IOException(e); + } + } + + public void dispose(@NonNull Resource accountData) { + try { + File file = accountData.getFile(); + if (!file.exists()) { + return; + } + try { + ZipFile zipFile = new ZipFile(file); + zipFile.close(); + } catch (ZipException notAZipFile) { + throw new IllegalArgumentException("The provided resource is not a ZIP file"); + } + log.debug("Deleting user data bundle {}", file.getAbsolutePath()); + file.delete(); + } catch (FileNotFoundException notAFileSystemResource) { + // ignore + } catch (IOException e) { + log.warn("Unable to dereference resource to delete", e); + } + } + + public @VisibleForTesting static @Value class UserDataBundle { + private Path folder; + private Path metadataDirectory; + private Path geodocsDirectory; + private Path extractorCsvFile; + private Path ogcstatsCsvFile; + } + + private Path buildCompressedUserDataResource(@NonNull Account account) throws Exception { + + final Path bundleFolder = Files.createTempDirectory(account.getUid()); + try { + final UserDataBundle uncompressedBundle = buildUserDataBundle(account, bundleFolder); + final Path compressedBundle = GDPRAccountWorker.createZipFile(uncompressedBundle.getFolder()); + + return compressedBundle; + } finally { + try { + FileUtils.deleteDirectory(bundleFolder.toFile()); + log.info("Directory {} deleted", bundleFolder.toAbsolutePath()); + } catch (IOException ioe) { + log.warn("Error deleting directory {}", bundleFolder.toAbsolutePath(), ioe); + } + } + } + + /** + *

+     * {@code
+     * .
+     * ├── account_info.ldif -> Text file in LDAP's LDIF format with the available user details
+     * ├── data_extractions_log.csv -> CSV file with details of data extractions performed by the user
+     * ├── ogc_request_log.csv -> CSV file with details of OGC service requests performed by the user
+     * ├── metadata -> Directory of metadata records contributed by the user, one file per record.
+     * │    ├── YYYY-MM-DD__.xml (e.g. 2019-07-01_ISO19139_1000.xml)
+     * │    ├── YYYY-MM-DD__.xml (e.g. 2019-07-01_ISO19139_1001.xml)
+     * │    └── ...
+     * ├── geodocs -> Directory of documents saved by the user, one file per doc plus one CSV file with additional info for each doc
+     *      ├── geodocs.csv -> CSV file with per-file hash record statistics
+     *      ├── YYYY-MM-DD__.xml (e.g. 2019-07-01_abc123.sld)
+     *      ├── YYYY-MM-DD__.xml (e.g. 2019-07-01_abc124.gml)
+     *      └── ...
+     * }
+     * 
+ */ + public @VisibleForTesting UserDataBundle buildUserDataBundle(@NonNull Account account, @NonNull Path bundleFolder) + throws Exception { + log.info("Creating GDPR data bundle for account {}", account.getUid()); + final String lidfContent = userInfoExporter.exportAsLdif(account); + + Files.write(bundleFolder.resolve("account_info.ldif"), Collections.singleton(lidfContent)); + + final Path metadataDirectory = bundleFolder.resolve("metadata"); + final Path geodocsDirectory = bundleFolder.resolve("geodocs"); + final Path extractorCsvFile = bundleFolder.resolve("data_extractions_log.csv"); + final Path ogcstatsCsvFile = bundleFolder.resolve("ogc_request_log.csv"); + + try { + Files.createDirectory(metadataDirectory); + } catch (IOException e) { + throw new IllegalStateException(e); + } + + final @Cleanup ContentProducer metadata = new MetadataProducer(metadataDirectory); + final @Cleanup ContentProducer docs = new GeodocsProducer(geodocsDirectory); + final @Cleanup ContentProducer extractor = new ExtractorProducer(extractorCsvFile); + final @Cleanup ContentProducer ogcStats = new OgcStatisticsProducer(ogcstatsCsvFile); + + CompletableFuture extractorTask; + CompletableFuture geodocsTask; + CompletableFuture mdTask; + CompletableFuture statsTask; + + extractorTask = runAsync(() -> accountGDPRDao.visitExtractorRecords(account, extractor)); + geodocsTask = runAsync(() -> accountGDPRDao.visitGeodocsRecords(account, docs)); + mdTask = runAsync(() -> accountGDPRDao.visitMetadataRecords(account, metadata)); + statsTask = runAsync(() -> accountGDPRDao.visitOgcStatsRecords(account, ogcStats)); + + CompletableFuture allTasks = CompletableFuture.allOf(extractorTask, geodocsTask, mdTask, statsTask); + + CompletableFuture bundleFuture = allTasks.thenApply(v -> new UserDataBundle(bundleFolder, + metadataDirectory, geodocsDirectory, extractorCsvFile, ogcstatsCsvFile)); + + return bundleFuture.join(); + } + + private static Path createZipFile(@NonNull Path bundleFolder) { + File rootDir = bundleFolder.toFile(); + File zip = bundleFolder.getParent().resolve(bundleFolder.getFileName() + ".zip").toFile(); + log.info("Compressing user data from {} to {}", rootDir.getAbsolutePath(), zip.getAbsolutePath()); + ZipUtil.pack(rootDir, zip); + log.info("User data zip file created successfully"); + return zip.toPath(); + } + + private static interface ContentProducer extends Consumer, AutoCloseable { + static R ifNonNull(@Nullable V value, Function mapper) { + return value == null ? null : mapper.apply(value); + } + } + + private static abstract class CsvContentProducer implements ContentProducer { + + private Path csvFile; + private CSVPrinter csvWriter; + + static final CSVFormat FORMAT = CSVFormat.RFC4180.withQuoteMode(QuoteMode.MINIMAL); + + protected CsvContentProducer(@NonNull Path target) { + this.csvFile = target; + try { + Files.createDirectories(target.getParent()); + Files.createFile(csvFile); + this.csvWriter = FORMAT.print(target, StandardCharsets.UTF_8); + this.csvWriter.printRecord(createHeader()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + public @Override void close() { + try { + csvWriter.close(); + } catch (IOException e) { + log.warn(e.getMessage(), e); + } + } + + public @Override void accept(T record) { + try { + List values = encode(record); + if (!values.isEmpty()) + csvWriter.printRecord(values); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + protected abstract List createHeader(); + + protected abstract List encode(T record); + } + + private @RequiredArgsConstructor static class MetadataProducer implements ContentProducer { + + /** + * Directory where to save one metadata file per record + */ + private final Path directory; + + public @Override void accept(MetadataRecord record) { + LocalDateTime createdDate = record.getCreatedDate(); + if (createdDate == null || record.getDocumentContent() == null) { + return; + } + LocalDateTime createdAt = createdDate.atZone(ZoneId.systemDefault()).toLocalDateTime(); + String date = FILENAME_DATE_FORMAT.format(createdAt); + String fileName = String.format("%s_%s_%d.xml", date, record.getSchemaId(), record.getId()); + Path targetFile = directory.resolve(fileName); + try { + Files.write(targetFile, Collections.singleton(record.getDocumentContent())); + } catch (IOException e) { + log.error("Error writing medatata document {}", targetFile.toAbsolutePath(), e); + } + } + + public @Override void close() throws Exception { + // nothing to close, each md file is opened and closed atomically at accept() + } + } + + /** + * Receives a {@link Path} denoting a directory where to save a + * {@code geodocs.csv} file with one row per user's document, as well as one + * file with the actual document for each record. + */ + private static class GeodocsProducer extends CsvContentProducer { + + /** + * Directory where to save one the {@code geodocs.csv} file as well as one + * document (the actual geodoc) per record + */ + private final Path directory; + + public GeodocsProducer(Path directory) { + super(directory.resolve("geodocs.csv")); + this.directory = directory; + } + + protected @Override List createHeader() { + return Arrays.asList("created_at", "last_access", "standard", "access_count", "file_hash"); + } + + @Override + protected List encode(GeodocRecord record) { + LocalDateTime createdAt = record.getCreatedAt(); + if (null == createdAt) { + return Collections.emptyList(); + } + String date = FILENAME_DATE_FORMAT.format(createdAt); + String standard = record.getStandard() == null ? "unknown" : record.getStandard().toLowerCase(); + String fileName = String.format("%s_%s.%s", date, record.getFileHash(), standard); + Path docFile = directory.resolve(fileName); + try { + Files.write(docFile, Collections.singleton(record.getRawFileContent())); + } catch (IOException e) { + log.error("Error writing medatata document {}", docFile.toAbsolutePath(), e); + } + return Arrays.asList(// + ContentProducer.ifNonNull(record.getCreatedAt(), CONTENT_DATE_FORMAT::format), // + ContentProducer.ifNonNull(record.getLastAccess(), CONTENT_DATE_FORMAT::format), // + record.getStandard(), // + record.getAccessCount(), // + record.getFileHash()// + ); + } + } + + private static class ExtractorProducer extends CsvContentProducer { + private static final WKTWriter WKT_WRITER = new WKTWriter(); + + protected ExtractorProducer(@NonNull Path target) { + super(target); + } + + protected @Override List createHeader() { + return Arrays.asList("creation_date", "duration", "organization", "roles", "success", "layer_name", + "format", "projection", "resolution", "bounding_box", "OWS_type", "URL"); + } + + protected @Override List encode(ExtractorRecord record) { + String bbox = ContentProducer.ifNonNull(record.getBbox(), box -> WKT_WRITER.write(box)); + String owsurl = record.getOwsurl(); + return Arrays.asList(// + ContentProducer.ifNonNull(record.getCreationDate(), CONTENT_DATE_FORMAT::format), // + ContentProducer.ifNonNull(record.getDuration(), + d -> DateTimeFormatter.ISO_TIME.format(d.toLocalTime())), // + record.getOrg(), // + ContentProducer.ifNonNull(record.getRoles(), + roles -> roles.stream().collect(Collectors.joining("|"))), // + record.isSuccess(), // + record.getLayerName(), // + record.getFormat(), // + record.getProjection(), // + record.getResolution(), // + bbox, // + record.getOwstype(), // + owsurl); + } + } + + private static class OgcStatisticsProducer extends CsvContentProducer { + + protected OgcStatisticsProducer(@NonNull Path target) { + super(target); + } + + protected @Override List createHeader() { + return Arrays.asList("date", "organization", "roles", "layer", "service", "request"); + } + + protected @Override List encode(OgcStatisticsRecord record) { + return Arrays.asList(// + ContentProducer.ifNonNull(record.getDate(), CONTENT_DATE_FORMAT::format), // + record.getOrg(), // + ContentProducer.ifNonNull(record.getRoles(), + roles -> roles.stream().collect(Collectors.joining("|"))), // + record.getLayer(), // + record.getService(), // + record.getRequest()); + } + + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/users/UserInfoExporter.java b/console/src/main/java/org/georchestra/console/ws/backoffice/users/UserInfoExporter.java new file mode 100644 index 0000000000..54d8276f60 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/users/UserInfoExporter.java @@ -0,0 +1,17 @@ +package org.georchestra.console.ws.backoffice.users; + +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.users.Account; +import org.springframework.ldap.NameNotFoundException; + +public interface UserInfoExporter { + + String exportAsLdif(String user) throws NameNotFoundException, DataServiceException; + + String exportAsLdif(Account account); + + String exportUsersAsCsv(String... userNames) throws DataServiceException; + + String exportUsersAsVcard(String... users) throws Exception; + +} \ No newline at end of file diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/users/UserInfoExporterImpl.java b/console/src/main/java/org/georchestra/console/ws/backoffice/users/UserInfoExporterImpl.java new file mode 100644 index 0000000000..1a9c10e566 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/users/UserInfoExporterImpl.java @@ -0,0 +1,152 @@ +package org.georchestra.console.ws.backoffice.users; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nullable; + +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.orgs.OrgsDao; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.AccountDao; +import org.ldaptive.BindConnectionInitializer; +import org.ldaptive.ConnectionConfig; +import org.ldaptive.ConnectionFactory; +import org.ldaptive.Credential; +import org.ldaptive.DefaultConnectionFactory; +import org.ldaptive.LdapException; +import org.ldaptive.SearchExecutor; +import org.ldaptive.SearchResult; +import org.ldaptive.io.LdifWriter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.ldap.NameNotFoundException; + +import ezvcard.VCard; +import ezvcard.parameter.EmailType; +import ezvcard.parameter.TelephoneType; +import ezvcard.property.Address; +import ezvcard.property.FormattedName; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +/** + * Service to export user account information in different formats (e.g. CSV, + * V-CARD, LDIF). + */ +@Slf4j +public class UserInfoExporterImpl implements UserInfoExporter { + + private @Value("${ldapScheme}://${ldapHost}:${ldapPort}") String ldapUrl; + private @Value("${ldapAdminDn:cn=admin,dc=georchestra,dc=org}") String ldapUserName; + private @Value("${ldapAdminPassword:secret}") String ldapPassword; + private @Value("${ldapUsersRdn:ou=users},${ldapBaseDn:dc=georchestra,dc=org}") String userSearchBaseDn; + + private AccountDao accountDao; + private OrgsDao orgsDao; + + public @Autowired UserInfoExporterImpl(AccountDao accountDao, OrgsDao orgsDao) { + this.accountDao = accountDao; + this.orgsDao = orgsDao; + } + + @Override + public String exportAsLdif(@NonNull String user) throws NameNotFoundException, DataServiceException { + Account a = accountDao.findByUID(user); + return exportAsLdif(a); + } + + @Override + public String exportAsLdif(@NonNull Account account) { + log.info("Exporting personal user data for account {}", account.getUid()); + ConnectionConfig connConfig = new ConnectionConfig(this.ldapUrl); + // connConfig.setUseStartTLS("true".equals(this.getUseStartTLS())); + connConfig.setConnectionInitializer( + new BindConnectionInitializer(this.ldapUserName, new Credential(this.ldapPassword))); + ConnectionFactory cf = new DefaultConnectionFactory(connConfig); + SearchResult result; + final String filter = String.format("(uid=%s)", account.getUid()); + try { + SearchExecutor executor = new SearchExecutor(); + executor.setBaseDn(this.userSearchBaseDn); + log.info("Running search '{}'", filter); + result = executor.search(cf, filter).getResult(); + log.info("User query successful: {}", result); + } catch (LdapException e) { + log.error("Error running {}", filter, e); + throw new IllegalStateException(e); + } + + StringWriter writer = new StringWriter(); + try { + new LdifWriter(writer).write(result); + } catch (IOException e) { + log.error("Error generating LDIF file", e); + throw new IllegalStateException(e); + } + String ldifContents = writer.toString(); + log.info("Returning LDIF: {}", ldifContents); + return ldifContents; + } + + private @NonNull String toVcf(@NonNull Account account) { + VCard v = new VCard(); + FormattedName f = new FormattedName(account.getGivenName() + " " + account.getSurname()); + v.addFormattedName(f); + v.addEmail(account.getEmail(), EmailType.WORK); + v.addTelephoneNumber(account.getPhone(), TelephoneType.WORK); + v.addTitle(account.getTitle()); + Address a = new Address(); + a.setPostalCode(account.getPostalCode()); + a.setStreetAddress(account.getPostalAddress()); + a.setPoBox(account.getPostOfficeBox()); + a.setLocality(account.getLocality()); + v.addAddress(a); + v.addTelephoneNumber(account.getMobile(), TelephoneType.CELL); + + return v.write(); + } + + @Override + public @NonNull String exportUsersAsCsv(@NonNull String... userNames) throws DataServiceException { + List accounts = new ArrayList<>(); + for (String name : userNames) { + Account a = getAccount(name); + if (a != null) { + accounts.add(a); + } + } + StringBuilder target = new StringBuilder(); + try { + new CSVAccountExporter(orgsDao).export(accounts, target); + } catch (IOException e) { + throw new DataServiceException(e); + } + return target.toString(); + } + + private @Nullable Account getAccount(@NonNull String user) throws DataServiceException { + try { + Account a = accountDao.findByUID(user); + return a; + } catch (NameNotFoundException e) { + log.error("User [{}] not found, skipping", user, e); + } + return null; + } + + @Override + public @NonNull String exportUsersAsVcard(@NonNull String... users) throws Exception { + StringBuilder ret = new StringBuilder(); + for (String user : users) { + Account a = getAccount(user); + if (a != null) { + ret.append(toVcf(a)); + } + } + + return ret.toString(); + } +} \ No newline at end of file diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/users/UsersController.java b/console/src/main/java/org/georchestra/console/ws/backoffice/users/UsersController.java new file mode 100644 index 0000000000..2d9b1c363b --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/users/UsersController.java @@ -0,0 +1,883 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.users; + +import java.io.IOException; +import java.text.Normalizer; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.mail.MessagingException; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.console.dao.AdvancedDelegationDao; +import org.georchestra.console.dao.DelegationDao; +import org.georchestra.console.dto.SimpleAccount; +import org.georchestra.console.mailservice.EmailFactory; +import org.georchestra.console.model.AdminLogType; +import org.georchestra.console.model.DelegationEntry; +import org.georchestra.console.ws.backoffice.users.GDPRAccountWorker.DeletedAccountSummary; +import org.georchestra.console.ws.backoffice.utils.RequestUtil; +import org.georchestra.console.ws.backoffice.utils.ResponseUtil; +import org.georchestra.console.ws.utils.LogUtils; +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.orgs.Org; +import org.georchestra.ds.orgs.OrgsDao; +import org.georchestra.ds.roles.Role; +import org.georchestra.ds.roles.RoleDao; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.AccountDao; +import org.georchestra.ds.users.AccountFactory; +import org.georchestra.ds.users.DuplicatedEmailException; +import org.georchestra.ds.users.DuplicatedUidException; +import org.georchestra.ds.users.ProtectedUserFilter; +import org.georchestra.ds.users.UserRule; +import org.georchestra.ds.users.UserSchema; +import org.georchestra.lib.file.FileUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.access.prepost.PostFilter; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import lombok.Setter; + +/** + * Web Services to maintain the User information. + * + *

+ * This class provides the operations to access the data layer to update and + * read the user data. Those operations will be consistent with the business + * rules. + *

+ * + * @author Mauricio Pazos + * + */ +@Controller +public class UsersController { + + private static final Log LOG = LogFactory.getLog(UsersController.class.getName()); + + private static final String BASE_MAPPING = "/private"; + private static final String REQUEST_MAPPING = BASE_MAPPING + "/users"; + private static final String PUBLIC_REQUEST_MAPPING = "/public/users"; + private static GrantedAuthority ROLE_SUPERUSER = AdvancedDelegationDao.ROLE_SUPERUSER; + + @Value("${gdpr.allowAccountDeletion:true}") + private Boolean gdprAllowAccountDeletion; + + private AccountDao accountDao; + + @Autowired + private OrgsDao orgDao; + + @Autowired + private RoleDao roleDao; + + @Autowired + private DelegationDao delegationDao; + + @Autowired + private AdvancedDelegationDao advancedDelegationDao; + + @Autowired + private @Setter GDPRAccountWorker gdprInfoWorker; + + @Autowired + private Boolean warnUserIfUidModified = false; + + private UserRule userRule; + + @Autowired + private EmailFactory emailFactory; + + @Autowired + protected LogUtils logUtils; + + public void setEmailFactory(EmailFactory emailFactory) { + this.emailFactory = emailFactory; + } + + public void setOrgDao(OrgsDao orgDao) { + this.orgDao = orgDao; + } + + public void setDelegationDao(DelegationDao delegationDao) { + this.delegationDao = delegationDao; + } + + public void setAdvancedDelegationDao(AdvancedDelegationDao advancedDelegationDao) { + this.advancedDelegationDao = advancedDelegationDao; + } + + public void setRoleDao(RoleDao roleDao) { + this.roleDao = roleDao; + } + + public void setWarnUserIfUidModified(boolean warnUserIfUidModified) { + this.warnUserIfUidModified = warnUserIfUidModified; + } + + public void setGdprAllowAccountDeletion(Boolean gdprAllowAccountDeletion) { + this.gdprAllowAccountDeletion = gdprAllowAccountDeletion; + } + + @Autowired + public UsersController(AccountDao dao, UserRule userRule) { + this.accountDao = dao; + this.userRule = userRule; + } + + /** + * Returns array of users using json syntax. + * + *
+     *
+     *	[
+     *	    {
+     *	        "org": "Zogak",
+     *	        "givenName": "Walsh",
+     *	        "sn": "Atkins",
+     *	        "uid": "watkins"
+     *	    },
+     *	        ...
+     *	]
+     * 
+ * + * @throws IOException + */ + @RequestMapping(value = REQUEST_MAPPING, method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + @PostFilter("hasPermission(filterObject, 'read')") + public List findAll() throws DataServiceException { + + ProtectedUserFilter filter = new ProtectedUserFilter(this.userRule.getListUidProtected()); + List list = this.accountDao.findFilterBy(filter); + Collections.sort(list); + + // Retrieve organizations list to display org name instead of org DN + List orgs = this.orgDao.findAll(); + Map orgNames = new HashMap<>(); + for (Org org : orgs) + orgNames.put(org.getId(), org.getName()); + + List res = new LinkedList<>(); + for (Account account : list) { + SimpleAccount simpleAccount = new SimpleAccount(account); + // Set Org Name with the human readable org name + simpleAccount.setOrgName(orgNames.get(account.getOrg())); + res.add(simpleAccount); + } + + return res; + } + + /** + * Returns the detailed information of the user. + * + *

+ * If the user identifier is not present in the ldap store an + * {@link IOException} will be throw. + *

+ *

+ * URL Format: [BASE_MAPPING]/users/{uid} + *

+ *

+ * Example: [BASE_MAPPING]/users/hsimpson + *

+ * + */ + @RequestMapping(value = REQUEST_MAPPING + + "/{uid:.+}", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + @ResponseBody + public Account findByUid(@PathVariable String uid) + throws AccessDeniedException, NameNotFoundException, DataServiceException { + + // Check for protected accounts + if (this.userRule.isProtected(uid)) + throw new AccessDeniedException("The user is protected: " + uid); + + // Check delegation + this.checkAuthorization(uid); + + return this.accountDao.findByUID(uid); + + } + + /** + * Returns the profile of current user. + * + *

+ * URL Format: [BASE_MAPPING]/users/profile + *

+ * + * returns following format : + * + *
+     * {
+     *   uid: "testuser",
+     *   org: "psc",
+     *   roles: ["USER", "MOD_EXTRACTORAPP"]
+     * }
+     * 
+ */ + @GetMapping(value = REQUEST_MAPPING + "/profile", produces = "application/json; charset=utf-8") + @ResponseBody + public String myProfile(HttpServletRequest request) throws JSONException, DataServiceException { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + List roles = auth.getAuthorities().stream().map(GrantedAuthority::getAuthority) + .map(s -> s.replaceFirst("ROLE_", "")).collect(Collectors.toList()); + Account a = accountDao.findByUID(auth.getName()); + + JSONObject res = new JSONObject(); + res.put("uid", auth.getName()); + res.put("roles", roles); + res.put("org", a.getOrg()); + + return res.toString(); + } + + /** + *

+ * Creates a new user. + *

+ * + *
+     * Request
+     *
+     * user data:
+     * {
+     *  "sn": "surname",
+     *	"givenName": "first name",
+     *	"mail": "e-mail",
+     * 	"telephoneNumber": "telephone"
+     *	"facsimileTelephoneNumber": "value",
+     * 	"street": "street",
+     * 	"postalCode": "postal code",
+     *	"l": "locality",
+     * 	"postOfficeBox": "the post office box",
+     *  "org": "the_organization"
+     * }
+     *
+     * where sn, givenName, mail are mandatories
+     * 
+ * + *
+     * Response
+     *
+     * - Success case
+     *
+     * The generated uid is added to the user data. So, a succeeded response should look like:
+     * {
+     * 	"uid": generated uid
+     *
+     *  "sn": "surname",
+     *	"givenName": "first name",
+     *	"mail": "e-mail",
+     * 	"telephoneNumber": "telephone"
+     *	"facsimileTelephoneNumber": "value",
+     * 	"street": "street",
+     * 	"postalCode": "postal code",
+     *	"l": "locality",
+     * 	"postOfficeBox": "the post office box"
+     * }
+     * 
+ * + *
+     * - Error case
+     * If the provided e-mail exists in the LDAP store the response will contain:
+     *
+     * 	{ \"success\": false, \"error\": \"duplicated_email\"}
+     *
+     * Error: 409 conflict with the current state of resource
+     *
+     * 
+ * + * @param request HTTP POST data contains the user data + * @throws IOException + */ + @RequestMapping(value = REQUEST_MAPPING, method = RequestMethod.POST, produces = "application/json; charset=utf-8") + @ResponseBody + public Account create(HttpServletRequest request) + throws IOException, DuplicatedEmailException, DataServiceException, DuplicatedUidException { + + final Account account = createAccountFromRequestBody(request.getInputStream()); + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + // Verify that org is under delegation if user is not SUPERUSER + final String requestOriginator = auth.getName(); + if (!callerIsSuperUser()) { + DelegationEntry delegation = this.delegationDao.findOne(requestOriginator); + if (delegation != null && !Arrays.asList(delegation.getOrgs()).contains(account.getOrg())) + throw new AccessDeniedException("Org not under delegation"); + } + + if (this.userRule.isProtected(account.getUid())) { + throw new AccessDeniedException("The user is protected: " + account.getUid()); + } + + // Saves the user in the LDAP + accountDao.insert(account); + + roleDao.addUser(Role.USER, account); + + orgDao.linkUser(account); + + logUtils.createLog(account.getUid(), AdminLogType.USER_CREATED, null); + + return account; + } + + public boolean callerIsSuperUser() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + return auth != null && auth.getAuthorities().contains(ROLE_SUPERUSER); + } + + /** + * Modifies the user data using the fields provided in the request body. + *

+ * The fields that are not present in the parameters will remain untouched in + * the LDAP store. + *

+ *

+ * The request format is: [BASE_MAPPING]/users/{uid} + *

+ *

+ * The request body should contains a the fields to modify using the JSON + * syntax. + *

+ *

+ * Example: + *

+ * + *
+     * Request
+     * [BASE_MAPPING]/users/hsimpson
+     *
+     * Body request: 
+     * {"sn": "surname",
+     *  "givenName": "first name",
+     *  "mail": "e-mail",
+     *  "telephoneNumber": "telephone",
+     *  "facsimileTelephoneNumber": "value",
+     * 	"street": "street",
+     *  "postalCode": "postal code",
+     *  "l": "locality",
+     *  "postOfficeBox": "the post office box"
+     * }
+     *
+     * 
+ * + * @param request + * + * @throws IOException if the uid does not exist or fails to access to + * the LDAP store. + * @throws NameNotFoundException + */ + @RequestMapping(value = REQUEST_MAPPING + + "/{uid:.+}", method = RequestMethod.PUT, produces = "application/json; charset=utf-8") + @ResponseBody + public Account update(@PathVariable String uid, HttpServletRequest request) + throws IOException, NameNotFoundException, DataServiceException, DuplicatedEmailException, ParseException, + JSONException, MessagingException { + + if (this.userRule.isProtected(uid)) { + throw new AccessDeniedException("The user is protected, it cannot be updated: " + uid); + } + + // check if user is under delegation for delegated admins + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + this.checkAuthorization(uid); + + // searches the account + Account originalAcount = this.accountDao.findByUID(uid); + Account modifiedAccount = modifyAccount(AccountFactory.create(originalAcount), request.getInputStream()); + boolean isPendingValidation = originalAcount.isPending() && !modifiedAccount.isPending(); + + if (!modifiedAccount.getOrg().equals(originalAcount.getOrg())) { + if (!auth.getAuthorities().contains(ROLE_SUPERUSER)) + if (!Arrays.asList(this.delegationDao.findOne(auth.getName()).getOrgs()) + .contains(originalAcount.getOrg())) + throw new AccessDeniedException("User not under delegation"); + orgDao.unlinkUser(originalAcount); + } + + accountDao.update(originalAcount, modifiedAccount); + + // log update modifications + logUtils.logChanges(modifiedAccount, originalAcount); + + if (!modifiedAccount.getOrg().equals(originalAcount.getOrg())) { + if (!auth.getAuthorities().contains(ROLE_SUPERUSER)) + if (!Arrays.asList(this.delegationDao.findOne(auth.getName()).getOrgs()) + .contains(modifiedAccount.getOrg())) + throw new AccessDeniedException("User not under delegation"); + orgDao.linkUser(modifiedAccount); + } + + if (accountDao.hasUserDnChanged(originalAcount, modifiedAccount)) { + // account was validated by a moderator, notify user + if (isPendingValidation) { + // send validation email to user + this.emailFactory.sendAccountWasCreatedEmail(request.getSession().getServletContext(), + modifiedAccount.getEmail(), modifiedAccount.getCommonName(), modifiedAccount.getUid()); + } + roleDao.modifyUser(originalAcount, modifiedAccount); + + // log pending user validation + if (isPendingValidation) { + logUtils.createLog(modifiedAccount.getUid(), AdminLogType.PENDING_USER_ACCEPTED, null); + } + } + + if (accountDao.hasUserLoginChanged(originalAcount, modifiedAccount)) { + DelegationEntry delegationEntry = delegationDao.findOne(originalAcount.getUid()); + if (delegationEntry != null) { + delegationDao.delete(delegationEntry); + delegationEntry.setUid(modifiedAccount.getUid()); + delegationDao.save(delegationEntry); + } + } + if (accountDao.hasUserLoginChanged(originalAcount, modifiedAccount) && warnUserIfUidModified) { + this.emailFactory.sendAccountUidRenamedEmail(request.getSession().getServletContext(), + modifiedAccount.getEmail(), modifiedAccount.getCommonName(), modifiedAccount.getUid()); + } + return modifiedAccount; + } + + /** + * Deletes the user. + *

+ * The user account and its associated roles are removed from the LDAP database, + * as well as any delegation linkage. Additionally, all the GDPR sensitive data + * collected for the account is obfuscated so that its untraceable back to the + * user. + *

+ * The request format is: + * + *

+     * [BASE_MAPPING]/users/{uid}
+     * 
+ */ + @RequestMapping(value = REQUEST_MAPPING + "/{uid:.+}", method = RequestMethod.DELETE, produces = "application/json") + public void delete(@PathVariable String uid, HttpServletRequest request, HttpServletResponse response) + throws IOException, DataServiceException, NameNotFoundException { + + if (this.userRule.isProtected(uid)) { + throw new AccessDeniedException("The user is protected, it cannot be deleted: " + uid); + } + + // check if user is under delegation for delegated admins + this.checkAuthorization(uid); + + final Account account = accountDao.findByUID(uid); + deleteAccount(account); + ResponseUtil.writeSuccess(response); + } + + private void deleteAccount(Account account) throws DataServiceException { + accountDao.delete(account); + roleDao.deleteUser(account); + + // Also delete delegation if exists + if (delegationDao.findOne(account.getUid()) != null) { + delegationDao.delete(account.getUid()); + } + + // log when a user is removed according to pending status + if (account.isPending()) { + logUtils.createLog(account.getUid(), AdminLogType.PENDING_USER_REFUSED, null); + } else { + logUtils.createLog(account.getUid(), AdminLogType.USER_DELETED, null); + } + } + + /** + * Deletes the account of the calling user and obfuscates records of GDPR + * sensitive information. + * + * @return summary of records anonymized as a result + */ + @RequestMapping(method = RequestMethod.POST, value = "/account/gdpr/delete", produces = "application/json") + public ResponseEntity deleteCurrentUserAndGDPRData(HttpServletResponse response) + throws DataServiceException { + + /* + * Disabling this endpoint if the gdpr.allowAccountDeletion property is set to + * false. + */ + if (!gdprAllowAccountDeletion) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return null; + } + + String accountId = SecurityContextHolder.getContext().getAuthentication().getName(); + + final Account account = accountDao.findByUID(accountId); + + if (this.userRule.isProtected(account.getUid())) + throw new AccessDeniedException("The user is protected, it cannot be deleted: " + account.getUid()); + + LOG.info(String.format("GDPR: user %s requested to delete his records", accountId)); + + deleteAccount(account); + + DeletedAccountSummary summary = gdprInfoWorker.deleteAccountRecords(account); + + DeletedUserDataInfo responseValue = toPresentation(accountId, summary); + + return new ResponseEntity<>(responseValue, HttpStatus.OK); + } + + private DeletedUserDataInfo toPresentation(final String accountId, DeletedAccountSummary summary) { + DeletedUserDataInfo responseValue = DeletedUserDataInfo.builder().account(accountId)// + .metadata(summary.getMetadataRecords())// + .extractor(summary.getExtractorRecords())// + .geodocs(summary.getGeodocsRecords())// + .metadata(summary.getMetadataRecords())// + .ogcStats(summary.getOgcStatsRecords())// + .build(); + return responseValue; + } + + @RequestMapping(value = PUBLIC_REQUEST_MAPPING + "/requiredFields", method = RequestMethod.GET) + public void getUserCreationRequiredFields(HttpServletResponse response) throws IOException { + try { + JSONArray fields = new JSONArray(); + fields.put(UserSchema.UID_KEY); + fields.put(UserSchema.MAIL_KEY); + fields.put(UserSchema.SURNAME_KEY); + fields.put(UserSchema.GIVEN_NAME_KEY); + ResponseUtil.buildResponse(response, fields.toString(4), HttpServletResponse.SC_OK); + } catch (Exception e) { + LOG.error(e.getMessage()); + ResponseUtil.buildResponse(response, ResponseUtil.buildResponseMessage(false, e.getMessage()), + HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + throw new IOException(e); + } + } + + /** + * Modify only the account's fields that are present in the request body. + * + * @param account + * @param inputStream + * + * @return the modified account + * + * @throws IOException + */ + private Account modifyAccount(Account account, ServletInputStream inputStream) + throws IOException, JSONException, ParseException, IllegalArgumentException { + + String strUser = FileUtils.asString(inputStream); + JSONObject json = new JSONObject(strUser); + + String givenName = RequestUtil.getFieldValue(json, UserSchema.GIVEN_NAME_KEY); + if (givenName != null) { + account.setGivenName(givenName); + } + + String surname = RequestUtil.getFieldValue(json, UserSchema.SURNAME_KEY); + if (surname != null) { + account.setSurname(surname); + } + + String email = RequestUtil.getFieldValue(json, UserSchema.MAIL_KEY); + if (email != null) { + account.setEmail(email); + } + + String postalAddress = RequestUtil.getFieldValue(json, UserSchema.POSTAL_ADDRESS_KEY); + if (postalAddress != null) { + account.setPostalAddress(postalAddress); + } + + String postOfficeBox = RequestUtil.getFieldValue(json, UserSchema.POST_OFFICE_BOX_KEY); + if (postOfficeBox != null) { + account.setPostOfficeBox(postOfficeBox); + } + + String postalCode = RequestUtil.getFieldValue(json, UserSchema.POSTAL_CODE_KEY); + if (postalCode != null) { + account.setPostalCode(postalCode); + } + + String street = RequestUtil.getFieldValue(json, UserSchema.STREET_KEY); + if (street != null) { + account.setStreet(street); + } + + String locality = RequestUtil.getFieldValue(json, UserSchema.LOCALITY_KEY); + if (locality != null) { + account.setLocality(locality); + } + + String phone = RequestUtil.getFieldValue(json, UserSchema.TELEPHONE_KEY); + if (phone != null) { + account.setPhone(phone); + } + + String facsimile = RequestUtil.getFieldValue(json, UserSchema.FACSIMILE_KEY); + if (facsimile != null) { + account.setFacsimile(facsimile); + } + + String title = RequestUtil.getFieldValue(json, UserSchema.TITLE_KEY); + if (title != null) { + account.setTitle(title); + } + + String description = RequestUtil.getFieldValue(json, UserSchema.DESCRIPTION_KEY); + if (description != null) { + account.setDescription(description); + } + + String manager = RequestUtil.getFieldValue(json, UserSchema.MANAGER_KEY); + account.setManager(manager); + + String note = RequestUtil.getFieldValue(json, UserSchema.NOTE_KEY); + if (note != null) { + account.setNote(note); + } + + String context = RequestUtil.getFieldValue(json, UserSchema.CONTEXT_KEY); + if (context != null) { + account.setContext(context); + } + String saslUser = RequestUtil.getFieldValue(json, "saslUser"); + if (saslUser != null) { + account.setSASLUser(saslUser); + } + + String commonName = AccountFactory.formatCommonName(account.getGivenName(), account.getSurname()); + account.setCommonName(commonName); + + String uid = RequestUtil.getFieldValue(json, UserSchema.UID_KEY); + if (uid != null) { + account.setUid(uid); + } + + String org = RequestUtil.getFieldValue(json, UserSchema.ORG_KEY); + if (org != null) + account.setOrg(org); + + String shadowExpire = RequestUtil.getFieldValue(json, UserSchema.SHADOW_EXPIRE_KEY); + if (shadowExpire != null) { + if ("".equals(shadowExpire)) + account.setShadowExpire(null); + else + account.setShadowExpire((new SimpleDateFormat("yyyy-MM-dd")).parse(shadowExpire)); + } + + String privacyPolicyAgreementDate = RequestUtil.getFieldValue(json, + UserSchema.PRIVACY_POLICY_AGREEMENT_DATE_KEY); + if (privacyPolicyAgreementDate != null) { + if ("".equals(privacyPolicyAgreementDate)) + account.setPrivacyPolicyAgreementDate(null); + else + try { + account.setPrivacyPolicyAgreementDate(LocalDate.parse(privacyPolicyAgreementDate)); + } catch (DateTimeParseException e) { + LOG.error(e.getMessage()); + throw new IllegalArgumentException(e); + } + } + + try { + account.setPending(json.getBoolean(UserSchema.PENDING)); + } catch (JSONException e) { + } + + return account; + } + + /** + * Create a new account from the body request. + */ + private Account createAccountFromRequestBody(ServletInputStream is) throws IllegalArgumentException, IOException { + + JSONObject json; + try { + json = new JSONObject(FileUtils.asString(is)); + } catch (JSONException e) { + LOG.error(e.getMessage()); + throw new IOException(e); + } + + String givenName = RequestUtil.getFieldValue(json, UserSchema.GIVEN_NAME_KEY); + String surname = RequestUtil.getFieldValue(json, UserSchema.SURNAME_KEY); + String email = RequestUtil.getFieldValue(json, UserSchema.MAIL_KEY); + String postalAddress = RequestUtil.getFieldValue(json, UserSchema.POSTAL_ADDRESS_KEY); + String postOfficeBox = RequestUtil.getFieldValue(json, UserSchema.POST_OFFICE_BOX_KEY); + String postalCode = RequestUtil.getFieldValue(json, UserSchema.POSTAL_CODE_KEY); + String street = RequestUtil.getFieldValue(json, UserSchema.STREET_KEY); + String locality = RequestUtil.getFieldValue(json, UserSchema.LOCALITY_KEY); + String phone = RequestUtil.getFieldValue(json, UserSchema.TELEPHONE_KEY); + String facsimile = RequestUtil.getFieldValue(json, UserSchema.FACSIMILE_KEY); + String title = RequestUtil.getFieldValue(json, UserSchema.TITLE_KEY); + String description = RequestUtil.getFieldValue(json, UserSchema.DESCRIPTION_KEY); + String manager = RequestUtil.getFieldValue(json, UserSchema.MANAGER_KEY); + String note = RequestUtil.getFieldValue(json, UserSchema.NOTE_KEY); + String context = RequestUtil.getFieldValue(json, UserSchema.CONTEXT_KEY); + String org = RequestUtil.getFieldValue(json, UserSchema.ORG_KEY); + String sshKeys = RequestUtil.getFieldValue(json, UserSchema.SSH_KEY); + String[] sshKeysA = new String[0]; + String saslUser = RequestUtil.getFieldValue(json, "saslUser"); + + if (!StringUtils.isEmpty(sshKeys)) { + sshKeysA = sshKeys.split("\n"); // TODO what would be the most convenient delimiter ? + } + + if (givenName == null) { + throw new IllegalArgumentException("First Name is required"); + } + if (surname == null) { + throw new IllegalArgumentException("Last Name is required"); + } + if (email == null) { + throw new IllegalArgumentException("Email is required"); + } + // Use specified login if not empty + String uid = RequestUtil.getFieldValue(json, UserSchema.UID_KEY); + if (!StringUtils.hasLength(uid)) + try { + uid = createUid(givenName, surname); + } catch (DataServiceException e) { + LOG.error(e.getMessage()); + throw new IOException(e); + } + + String commonName = AccountFactory.formatCommonName(givenName, surname); + + UUID uuid = null; + String suuid = RequestUtil.getFieldValue(json, UserSchema.UUID_KEY); + if (StringUtils.hasLength(suuid)) { + uuid = UUID.fromString(suuid); + } + + Account a = AccountFactory.createFull(uuid, uid, commonName, surname, givenName, email, title, phone, + description, postalAddress, postalCode, "", postOfficeBox, "", street, locality, facsimile, "", "", "", + "", manager, note, context, org, sshKeysA, saslUser); + + String shadowExpire = RequestUtil.getFieldValue(json, UserSchema.SHADOW_EXPIRE_KEY); + if (StringUtils.hasLength(shadowExpire)) { + try { + a.setShadowExpire((new SimpleDateFormat("yyyy-MM-dd")).parse(shadowExpire)); + } catch (ParseException e) { + LOG.error(e.getMessage()); + throw new IllegalArgumentException(e); + } + } + + String privacyPolicyAgreementDate = RequestUtil.getFieldValue(json, + UserSchema.PRIVACY_POLICY_AGREEMENT_DATE_KEY); + if (StringUtils.hasLength(privacyPolicyAgreementDate)) { + try { + a.setPrivacyPolicyAgreementDate(LocalDate.parse(privacyPolicyAgreementDate)); + } catch (DateTimeParseException e) { + LOG.error(e.getMessage()); + throw new IllegalArgumentException(e); + } + } + + return a; + } + + /** + * Creates a uid based on the given name and surname + * + * @param givenName + * @param surname + * @return return the proposed uid + * + * @throws DataServiceException + */ + private String createUid(String givenName, String surname) throws DataServiceException { + + String proposedUid = normalizeString(givenName.toLowerCase().charAt(0) + surname.toLowerCase()); + + if (this.accountDao.exists(proposedUid)) { + return this.accountDao.generateUid(proposedUid); + } + return proposedUid; + } + + /** + * Check Authorization of current logged user against specified uid and throw a + * AccessDeniedException if current user is not SUPERUSER and user 'uid' is not + * under the delegation. + * + * @param uid Identifier of user to search in delegation of connected user + * + * @throws AccessDeniedException if current user does not have permission to + * edit user 'uid' + */ + private void checkAuthorization(String uid) { + // check if user is under delegation for delegated admins + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (!auth.getAuthorities().contains(AdvancedDelegationDao.ROLE_SUPERUSER)) + if (!this.advancedDelegationDao.findUsersUnderDelegation(auth.getName()).contains(uid)) + throw new AccessDeniedException("User " + uid + " not under delegation"); + } + + /** + * Deaccentuate a string and remove non-word characters + * + * references: http://stackoverflow.com/a/8523728 and + * http://stackoverflow.com/a/2397830 + * + * @param string an accentuated string, eg. "Joá+o" + * @return return the deaccentuated string, eg. "Joao" + */ + public static String normalizeString(String string) { + return Normalizer.normalize(string, Normalizer.Form.NFD).replaceAll("\\W", ""); + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/users/UsersExport.java b/console/src/main/java/org/georchestra/console/ws/backoffice/users/UsersExport.java new file mode 100644 index 0000000000..ee240fb003 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/users/UsersExport.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.users; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.georchestra.console.dao.AdvancedDelegationDao; +import org.georchestra.ds.DataServiceException; +import org.json.JSONArray; +import org.json.JSONException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import lombok.NonNull; + +@Controller +public class UsersExport { + + private @Autowired GDPRAccountWorker gdprInfoExporter; + + private UserInfoExporter accountInfoExporter; + + @Autowired + private AdvancedDelegationDao advancedDelegationDao; + + public @Autowired UsersExport(UserInfoExporter accountInfoExporter) { + this.accountInfoExporter = accountInfoExporter; + } + + /** + * Generates a ZIP file bundle with all the calling user's EU + * General Data Protection Regulation) + * relevant information available on the system. + */ + @RequestMapping(method = RequestMethod.GET, value = "/account/gdpr/download", produces = "application/zip") + public void downloadUserData(HttpServletResponse response) + throws NameNotFoundException, DataServiceException, IOException { + + final Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + final String userId = auth.getName(); + + Resource data = gdprInfoExporter.generateUserData(userId); + + int contentLength; + try { + File file = data.getFile(); + contentLength = (int) file.length(); + } catch (IOException notAFileResource) { + contentLength = 0; + } + try { + String fileName = userId + "_account_data.zip"; + response.setContentType("application/zip"); + response.setContentLength(contentLength); + response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + + IOUtils.copy(data.getInputStream(), response.getOutputStream()); + response.flushBuffer(); + } finally { + gdprInfoExporter.dispose(data); + } + } + + @PostMapping(value = "/private/export/users.csv", consumes = MediaType.APPLICATION_JSON_VALUE, produces = "text/csv; charset=utf-8") + @ResponseBody + public String getUsersAsCsv(@RequestBody String users) throws Exception { + String[] parsedUsers = parseUserNamesFromJSONArray(users); + checkAccessPermissionToUsersData(parsedUsers); + @NonNull + String csvUsers = accountInfoExporter.exportUsersAsCsv(parsedUsers); + return csvUsers; + } + + @PostMapping(value = "/private/export/users.vcf", consumes = MediaType.APPLICATION_JSON_VALUE, produces = "text/x-vcard; charset=utf-8") + @ResponseBody + public String getUsersAsVcard(@RequestBody String users) throws Exception { + String[] parsedUsers = parseUserNamesFromJSONArray(users); + checkAccessPermissionToUsersData(parsedUsers); + @NonNull + String vcards = accountInfoExporter.exportUsersAsVcard(parsedUsers); + return vcards; + } + + /** + * Parses and returns the user names given as a JSON array (e.g. + * {@code ["admin", "testuser"]}) + */ + private String[] parseUserNamesFromJSONArray(String rawUsers) { + JSONArray jsonUsers = new JSONArray(rawUsers); + String[] users = StreamSupport.stream(jsonUsers.spliterator(), false).toArray(String[]::new); + return users; + } + + /** + * Checks that the calling user has permissions to view data regarding the + * requested users + * + * @throws AccessDeniedException if current user does not have permissions to + * view data of all requested users + */ + private void checkAccessPermissionToUsersData(String[] users) throws JSONException { + // check if user is under delegation for delegated admins + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (!auth.getAuthorities().contains(AdvancedDelegationDao.ROLE_SUPERUSER)) { + Set usersUnderDelegation = this.advancedDelegationDao.findUsersUnderDelegation(auth.getName()); + List invalid = Arrays.stream(users).filter(u -> !usersUnderDelegation.contains(u)) + .collect(Collectors.toList()); + if (!invalid.isEmpty()) { + throw new AccessDeniedException("Some users are not under delegation: " + invalid); + } + } + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/utils/RequestUtil.java b/console/src/main/java/org/georchestra/console/ws/backoffice/utils/RequestUtil.java new file mode 100644 index 0000000000..d75cbf1cd4 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/utils/RequestUtil.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.utils; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Request useful methods to manage request data structure. + * + * @author Mauricio Pazos + * + */ +public class RequestUtil { + + private RequestUtil() { + // utility class + } + + /** + * Returns the value associated to the fieldName. + * + * If the fieldName value is not present in the JSON object a null value is + * returned. + * + * @param json + * @param fieldName + * + * @return the value + */ + public static String getFieldValue(final JSONObject json, final String fieldName) { + String v = json.optString(fieldName, null); + return "null".equals(v) ? null : v;// shocking but happens + } + + /** + * Returns the boolean value associated to the fieldName. + * + * @param json + * @param fieldName + * + * @return the value as boolean + */ + public static Boolean getBooleanFieldValue(final JSONObject json, final String fieldName) { + Boolean value; + try { + value = json.getBoolean(fieldName); + } catch (JSONException e) { + return false; + } + return value; + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/utils/Response.java b/console/src/main/java/org/georchestra/console/ws/backoffice/utils/Response.java new file mode 100644 index 0000000000..932869eca9 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/utils/Response.java @@ -0,0 +1,17 @@ +package org.georchestra.console.ws.backoffice.utils; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import lombok.Data; + +public @Data class Response { + + @JsonInclude(JsonInclude.Include.ALWAYS) + private boolean success; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private String error; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private Object response; +} diff --git a/console/src/main/java/org/georchestra/console/ws/backoffice/utils/ResponseUtil.java b/console/src/main/java/org/georchestra/console/ws/backoffice/utils/ResponseUtil.java new file mode 100644 index 0000000000..9ff7f040b2 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/backoffice/utils/ResponseUtil.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.backoffice.utils; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Utility class which contains useful method to prepare the http response. + * + * + * @author Mauricio Pazos + * + */ +final public class ResponseUtil { + + private ResponseUtil() { + // utility class pattern + } + + public static Response success() { + return success(null); + } + + public static Response success(@Nullable Object payload) { + Response response = new Response(); + response.setSuccess(true); + response.setResponse(payload); + return response; + } + + public static Response failure() { + return failure(null); + } + + public static Response failure(@Nullable String errorMessage) { + Response response = new Response(); + response.setSuccess(false); + response.setError(errorMessage); + return response; + } + + /** + * Build the success message + * + * @return success message + */ + public static String buildSuccessMessage() { + return buildResponseMessage(Boolean.TRUE, null); + } + + public static String buildResponseMessage(Boolean status) { + return buildResponseMessage(status, null); + } + + public static String buildResponseMessage(Boolean status, String errorMessage) { + + JSONObject res = new JSONObject(); + try { + res.put("success", status); + if (errorMessage != null) { + res.put("error", errorMessage); + } + } catch (JSONException e) { + throw new RuntimeException(e); + } + return res.toString(); + } + + public static void buildResponse(HttpServletResponse response, String jsonData, int sc) throws IOException { + + response.setContentType("application/json"); + response.setStatus(sc); + + PrintWriter out = response.getWriter(); + try { + out.println(jsonData); + + } finally { + out.close(); + } + } + + public static void writeSuccess(HttpServletResponse response) throws IOException { + + buildResponse(response, ResponseUtil.buildSuccessMessage(), HttpServletResponse.SC_OK); + } + + public static void writeError(HttpServletResponse response, String message) throws IOException { + + buildResponse(response, ResponseUtil.buildResponseMessage(Boolean.FALSE, message), + HttpServletResponse.SC_NOT_FOUND); + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/changepassword/ChangePasswordFormBean.java b/console/src/main/java/org/georchestra/console/ws/changepassword/ChangePasswordFormBean.java new file mode 100644 index 0000000000..20c02026f0 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/changepassword/ChangePasswordFormBean.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.changepassword; + +import java.io.Serializable; + +/** + * @author Mauricio Pazos + * + */ +public class ChangePasswordFormBean implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -546015147230737054L; + private String confirmPassword; + private String password; + + @Override + public String toString() { + return "ChangePasswordFormBean [confirmPassword=" + confirmPassword + ", password=" + password + "]"; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/changepassword/ChangePasswordFormController.java b/console/src/main/java/org/georchestra/console/ws/changepassword/ChangePasswordFormController.java new file mode 100644 index 0000000000..cd24af571a --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/changepassword/ChangePasswordFormController.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.changepassword; + +import org.georchestra.console.model.AdminLogType; +import org.georchestra.console.ws.utils.LogUtils; +import org.georchestra.console.ws.utils.PasswordUtils; +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.AccountDao; +import org.georchestra.ds.users.PasswordType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.SessionAttributes; + +import java.util.Optional; + +/** + * This controller is responsible of manage the user interactions required for + * changing the user account's password. + *

+ * This controller is associated to the changePasswordForm.jsp view and + * {@link ChangePasswordFormBean}. + *

+ * + * @author Mauricio Pazos + */ +@Controller +@SessionAttributes(types = ChangePasswordFormBean.class) +public class ChangePasswordFormController { + + private final AccountDao accountDao; + + @Autowired + protected PasswordUtils passwordUtils; + + @Autowired + protected LogUtils logUtils; + + @Autowired + public ChangePasswordFormController(AccountDao dao) { + this.accountDao = dao; + } + + @InitBinder + public void initForm(WebDataBinder dataBinder) { + dataBinder.setAllowedFields("password", "confirmPassword"); + } + + /** + * Initializes the {@link ChangePasswordFormBean} with the uid provided as + * parameter. The changePasswordForm view is provided as result of this method. + * + * @param model + * + * @return changePasswordForm view to display + * + * @throws DataServiceException + */ + @RequestMapping(value = "/account/changePassword", method = RequestMethod.GET) + public String setupForm(Model model) throws DataServiceException { + Optional uid = getUsername(); + if (uid.isPresent()) { + + if (isUserAuthenticatedBySASL(uid.get())) { + return "userManagedBySASL"; + } + + ChangePasswordFormBean formBean = new ChangePasswordFormBean(); + model.addAttribute(formBean); + return "changePasswordForm"; + } + return "forbidden"; + } + + /** + * Changes the password in the ldap store. + * + * @param model + * @param formBean + * @param result + * + * @return the next view + * + * @throws DataServiceException + */ + @RequestMapping(value = "/account/changePassword", method = RequestMethod.POST) + public String changePassword(Model model, @ModelAttribute ChangePasswordFormBean formBean, BindingResult result) + throws DataServiceException { + Optional username = getUsername(); + if (username.isPresent()) { + String uid = username.get(); + if (isUserAuthenticatedBySASL(uid)) { + return "userManagedBySASL"; + } + + passwordUtils.validate(formBean.getPassword(), formBean.getConfirmPassword(), result); + + if (result.hasErrors()) { + return "changePasswordForm"; + } + + // change the user's password + String password = formBean.getPassword(); + this.accountDao.changePassword(uid, password); + model.addAttribute("success", true); + + // log that password was changed for this user + logUtils.createLog(uid, AdminLogType.USER_PASSWORD_CHANGED, null); + + return "changePasswordForm"; + } + return "forbidden"; + } + + @ModelAttribute("changePasswordFormBean") + public ChangePasswordFormBean getChangePasswordFormBean() { + return new ChangePasswordFormBean(); + } + + private Optional getUsername() { + try { + User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + return Optional.of(user.getUsername()); + } catch (NullPointerException ex) { + return Optional.empty(); + } + } + + private boolean checkPermission(String uid) { + try { + User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + return user.getUsername().equals(uid); + } catch (NullPointerException ex) { + return false; + } + } + + private boolean isUserAuthenticatedBySASL(String uid) throws DataServiceException { + // check if the user is managed by an external service. if so, then forbid + // access to change password. + Account d = this.accountDao.findByUID(uid); + return d.getPasswordType() == PasswordType.SASL; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/editorgdetails/EditOrgDetailsFormBean.java b/console/src/main/java/org/georchestra/console/ws/editorgdetails/EditOrgDetailsFormBean.java new file mode 100644 index 0000000000..c99d28cf52 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/editorgdetails/EditOrgDetailsFormBean.java @@ -0,0 +1,14 @@ +package org.georchestra.console.ws.editorgdetails; + +import lombok.Data; + +final @Data class EditOrgDetailsFormBean implements java.io.Serializable { + private static final long serialVersionUID = -5836489312467203512L; + private String id; + private String name; + private String shortName; + private String description; + private String address; + private String url; + private String orgType; +} diff --git a/console/src/main/java/org/georchestra/console/ws/editorgdetails/EditOrgDetailsFormController.java b/console/src/main/java/org/georchestra/console/ws/editorgdetails/EditOrgDetailsFormController.java new file mode 100644 index 0000000000..1b57f2e239 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/editorgdetails/EditOrgDetailsFormController.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ +package org.georchestra.console.ws.editorgdetails; + +import static org.georchestra.commons.security.SecurityHeaders.SEC_ORG; + +import java.io.IOException; +import java.util.Base64; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.commons.security.SecurityHeaders; +import org.georchestra.console.model.AdminLogType; +import org.georchestra.console.ws.utils.LogUtils; +import org.georchestra.console.ws.utils.Validation; +import org.georchestra.ds.orgs.Org; +import org.georchestra.ds.orgs.OrgsDao; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.multipart.MultipartFile; + +@Controller +@SessionAttributes(types = EditOrgDetailsFormBean.class) +public class EditOrgDetailsFormController { + private OrgsDao orgsDao; + private Validation validation; + private static final String[] FIELDS = { "id", "url", "description", "logo", "name", "address" }; + + private static final Log LOG = LogFactory.getLog(EditOrgDetailsFormController.class.getName()); + + @Autowired + protected LogUtils logUtils; + + @Autowired + public EditOrgDetailsFormController(OrgsDao orgsDao, Validation validation) { + this.orgsDao = orgsDao; + this.validation = validation; + } + + @InitBinder + public void initForm(WebDataBinder dataBinder) { + dataBinder.setAllowedFields(FIELDS); + } + + @ModelAttribute("editOrgDetailsFormBean") + public EditOrgDetailsFormBean getEditOrgDetailsFormBean() { + return new EditOrgDetailsFormBean(); + } + + /** + * Retrieves the org data and sets the model before presenting the edit form + * view. + * + * @param model + * @return the edit form view + * @throws IOException + */ + @RequestMapping(value = "/account/orgdetails", method = RequestMethod.GET) + @PreAuthorize("hasAnyRole('REFERENT', 'SUPERUSER')") + public String setupForm(HttpServletRequest request, Model model) { + Org org = this.orgsDao.findByCommonName(SecurityHeaders.decode(request.getHeader(SEC_ORG))); + model.addAttribute(createForm(org)); + model.addAttribute("name", org.getName()); + model.addAttribute("id", org.getId()); + model.addAttribute("logo", org.getLogo()); + HttpSession session = request.getSession(); + for (String f : FIELDS) { + if (validation.isOrgFieldRequired(f)) { + session.setAttribute(f + "Required", "true"); + } + } + return "editOrgDetailsForm"; + } + + @RequestMapping(value = "/account/orgdetails", method = RequestMethod.POST) + @PreAuthorize("hasAnyRole('REFERENT', 'SUPERUSER')") + public String edit(Model model, @ModelAttribute EditOrgDetailsFormBean formBean, + @RequestParam(name = "logo") MultipartFile logo, BindingResult resultErrors) throws IOException { + validation.validateOrgField("url", formBean.getUrl(), resultErrors); + validation.validateOrgField("address", formBean.getAddress(), resultErrors); + validation.validateOrgField("description", formBean.getDescription(), resultErrors); + + if (resultErrors.hasErrors()) { + return "editOrgDetailsForm"; + } + + Org orgOrigin = orgsDao.findByCommonName(formBean.getId()); + final Org orgOriginClone = orgOrigin.clone(); + + orgOrigin.setDescription(formBean.getDescription()); + orgOrigin.setUrl(formBean.getUrl()); + orgOrigin.setAddress(formBean.getAddress()); + + if (!logo.isEmpty()) { + orgOrigin.setLogo(transformLogoFileToBase64(logo)); + } + orgsDao.update(orgOrigin); + model.addAttribute("success", true); + + // log each attributes modifications + logOrgDetailsChanges(orgOriginClone, formBean, logo); + + return "redirect:/account/userdetails"; + + } + + private String transformLogoFileToBase64(MultipartFile logo) throws IOException { + byte[] base64Encoded = Base64.getMimeEncoder().encode(logo.getBytes()); + return new String(base64Encoded); + } + + private EditOrgDetailsFormBean createForm(Org org) { + EditOrgDetailsFormBean formBean = new EditOrgDetailsFormBean(); + formBean.setId(org.getId()); + formBean.setName(org.getName()); + formBean.setShortName(org.getShortName()); + formBean.setDescription(org.getDescription()); + formBean.setUrl(org.getUrl()); + formBean.setAddress(org.getAddress()); + formBean.setOrgType(org.getOrgType()); + return formBean; + } + + /** + * Method to compare attributes and create log for each attributes if attribute + * changes + * + * @param org organization to update + * @param formBean EditOrgDetailsFormBean to get information about user input + * @param logo MultipartFile spring to treat a picture as logo + */ + protected void logOrgDetailsChanges(Org org, EditOrgDetailsFormBean formBean, MultipartFile logo) { + AdminLogType type = AdminLogType.ORG_ATTRIBUTE_CHANGED; + String id = org.getId(); + + try { + if (logo != null && org.getLogo() != null && !org.getLogo().equals(transformLogoFileToBase64(logo))) { + logUtils.createAndLogDetails(id, Org.JSON_LOGO, null, null, type); + } + } catch (IOException e) { + LOG.info("Can't create admin log and detail for logo replacement.", e); + } + + if (!org.getDescription().equals(formBean.getDescription())) { + logUtils.createAndLogDetails(id, Org.JSON_DESCRIPTION, org.getDescription(), formBean.getDescription(), + type); + } + + if (!org.getUrl().equals(formBean.getUrl())) { + logUtils.createAndLogDetails(id, Org.JSON_URL, org.getUrl(), formBean.getUrl(), type); + } + + if (!org.getAddress().equals(formBean.getAddress())) { + logUtils.createAndLogDetails(id, Org.JSON_ADDRESS, org.getAddress(), formBean.getAddress(), type); + } + } + +} \ No newline at end of file diff --git a/console/src/main/java/org/georchestra/console/ws/edituserdetails/EditUserDetailsFormBean.java b/console/src/main/java/org/georchestra/console/ws/edituserdetails/EditUserDetailsFormBean.java new file mode 100644 index 0000000000..df4a3965ab --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/edituserdetails/EditUserDetailsFormBean.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.edituserdetails; + +/** + * @author Mauricio Pazos + * + */ +public class EditUserDetailsFormBean implements java.io.Serializable { + + private String uid; + private String surname; + private String firstName; + private String email; + private String title; + private String phone; + private String facsimile; + private String org; + private String description; + private String postalAddress; + + @Override + public String toString() { + return "EditUserDetailsFormBean [uid=" + uid + ", surname=" + surname + ", givenName=" + firstName + ", email=" + + email + ", title=" + title + ", phone=" + phone + ", facsimile=" + facsimile + ", org=" + org + + ", description=" + description + ", postalAddress=" + postalAddress + "]"; + } + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String givenName) { + this.firstName = givenName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getFacsimile() { + return facsimile; + } + + public void setFacsimile(String facsimile) { + this.facsimile = facsimile; + } + + public String getOrg() { + return org; + } + + public void setOrg(String org) { + this.org = org; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getPostalAddress() { + return postalAddress; + } + + public void setPostalAddress(String postalAddress) { + this.postalAddress = postalAddress; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/edituserdetails/EditUserDetailsFormController.java b/console/src/main/java/org/georchestra/console/ws/edituserdetails/EditUserDetailsFormController.java new file mode 100644 index 0000000000..0a95a8bf06 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/edituserdetails/EditUserDetailsFormController.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.edituserdetails; + +import static org.georchestra.commons.security.SecurityHeaders.SEC_USERNAME; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.georchestra.commons.security.SecurityHeaders; +import org.georchestra.console.ws.utils.LogUtils; +import org.georchestra.console.ws.utils.Validation; +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.orgs.Org; +import org.georchestra.ds.orgs.OrgsDao; +import org.georchestra.ds.roles.Role; +import org.georchestra.ds.roles.RoleDao; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.AccountDao; +import org.georchestra.ds.users.AccountImpl; +import org.georchestra.ds.users.DuplicatedEmailException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.bind.support.SessionStatus; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Support for the Edit Account user interactions. + * + * @author Mauricio Pazos + */ +@Controller +@SessionAttributes(types = EditUserDetailsFormBean.class) +public class EditUserDetailsFormController { + + private final RoleDao roleDao; + private final OrgsDao orgsDao; + private final AccountDao accountDao; + + private Validation validation; + + private @Value("${gdpr.allowAccountDeletion:true}") Boolean gdprAllowAccountDeletion; + + @Autowired + protected LogUtils logUtils; + + public void setGdprAllowAccountDeletion(Boolean gdprAllowAccountDeletion) { + this.gdprAllowAccountDeletion = gdprAllowAccountDeletion; + } + + @Autowired + public EditUserDetailsFormController(AccountDao dao, OrgsDao orgsDao, RoleDao roleDao, Validation validation) { + this.accountDao = dao; + this.orgsDao = orgsDao; + this.roleDao = roleDao; + this.validation = validation; + + } + + private static final String[] fields = { "uid", "firstName", "surname", "email", "title", "phone", "facsimile", + "org", "description", "postalAddress" }; + + @InitBinder + public void initForm(WebDataBinder dataBinder) { + + dataBinder.setAllowedFields(fields); + } + + /** + * Retrieves the account data and sets the model before presenting the edit form + * view. + * + * @param model + * @return the edit form view + * @throws IOException + */ + @RequestMapping(value = "/account/userdetails", method = RequestMethod.GET) + @PreAuthorize("isAuthenticated()") + public String setupForm(HttpServletRequest request, HttpServletResponse response, Model model) throws IOException { + try { + String username = SecurityHeaders.decode(request.getHeader(SEC_USERNAME)); + Account userAccount = this.accountDao.findByUID(username); + model.addAttribute(createForm(userAccount)); + Org org = orgsDao.findByUser(userAccount); + model.addAttribute("org", orgToJson(org)); + model.addAttribute("isReferentOrSuperUser", isReferentOrSuperUser(userAccount)); + model.addAttribute("gdprAllowAccountDeletion", gdprAllowAccountDeletion); + + HttpSession session = request.getSession(); + for (String f : fields) { + if (this.validation.isUserFieldRequired(f)) { + session.setAttribute(f + "Required", "true"); + } + } + + return "editUserDetailsForm"; + + } catch (Exception e) { + e.printStackTrace(); + throw new IOException(e); + } + } + + private boolean isReferentOrSuperUser(Account userAccount) throws DataServiceException { + List roles = roleDao.findAllForUser(userAccount); + return roles.stream().anyMatch(n -> (n.getName().equals("SUPERUSER") || n.getName().equals("REFERENT"))); + } + + /** + * Creates a form based on the account data. + * + * @param account input data + */ + EditUserDetailsFormBean createForm(final Account account) { + + EditUserDetailsFormBean formBean = new EditUserDetailsFormBean(); + + formBean.setUid(account.getUid()); + formBean.setEmail(account.getEmail()); + formBean.setFirstName(account.getGivenName()); + formBean.setSurname(account.getSurname()); + formBean.setTitle(account.getTitle()); + formBean.setPhone(account.getPhone()); + formBean.setFacsimile(account.getFacsimile()); + formBean.setDescription(account.getDescription()); + formBean.setPostalAddress(account.getPostalAddress()); + String org = account.getOrg(); + if (!org.equals("")) { + formBean.setOrg(orgsDao.findByCommonName(org).getName()); + } + + return formBean; + } + + /** + * Generates a new password, then an e-mail is sent to the user to inform that a + * new password is available. + * + * @param formBean Contains the user's email + * @param resultErrors will be updated with the list of found errors. + * @param sessionStatus + * @return the next view + * @throws IOException + */ + @RequestMapping(value = "/account/userdetails", method = RequestMethod.POST) + public String edit(HttpServletRequest request, HttpServletResponse response, Model model, + @ModelAttribute EditUserDetailsFormBean formBean, BindingResult resultErrors, SessionStatus sessionStatus) + throws IOException { + String uid = formBean.getUid(); + try { + String username = SecurityHeaders.decode(request.getHeader(SEC_USERNAME)); + if (!username.equals(uid)) + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } catch (NullPointerException e) { + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } + + model.addAttribute("gdprAllowAccountDeletion", gdprAllowAccountDeletion); + + // Validate first name and surname + validation.validateUserFieldWithSpecificMsg("firstName", formBean.getFirstName(), resultErrors); + validation.validateUserFieldWithSpecificMsg("surname", formBean.getSurname(), resultErrors); + + validation.validateUserField("phone", formBean.getPhone(), resultErrors); + validation.validateUserField("facsimile", formBean.getFacsimile(), resultErrors); + validation.validateUserField("title", formBean.getTitle(), resultErrors); + validation.validateUserField("description", formBean.getDescription(), resultErrors); + validation.validateUserField("postalAddress", formBean.getPostalAddress(), resultErrors); + + if (resultErrors.hasErrors()) + return "editUserDetailsForm"; + + // updates the account details + try { + + String username = SecurityHeaders.decode(request.getHeader(SEC_USERNAME)); + Account originalAccount = this.accountDao.findByUID(username); + Account modifiedAccount = this.accountDao.findByUID(username); + Account account = modify(modifiedAccount, formBean); + accountDao.update(account); + + model.addAttribute("success", true); + Org org = orgsDao.findByUser(account); + model.addAttribute("org", orgToJson(org)); + model.addAttribute("isReferentOrSuperUser", isReferentOrSuperUser(account)); + + // create log for each account modification + if (logUtils != null) { + logUtils.logChanges(modifiedAccount, originalAccount); + } + return "editUserDetailsForm"; + + } catch (DuplicatedEmailException e) { + + // right now the email cannot be edited (review requirement) + // resultErrors.addError(new ObjectError("email", "Exist a user with this + // e-mail")); + return "createAccountForm"; + + } catch (DataServiceException e) { + + throw new IOException(e); + } + + } + + /** + * Modifies the account using the values present in the formBean parameter + * + * @param account + * @param formBean + * @return modified account + */ + private Account modify(Account account, EditUserDetailsFormBean formBean) { + account.setGivenName(formBean.getFirstName()); + account.setSurname(formBean.getSurname()); + account.setTitle(formBean.getTitle()); + account.setPhone(formBean.getPhone()); + account.setFacsimile(formBean.getFacsimile()); + account.setDescription(formBean.getDescription()); + account.setPostalAddress(formBean.getPostalAddress()); + + return account; + } + + @ModelAttribute("editUserDetailsFormBean") + public EditUserDetailsFormBean getEditUserDetailsFormBean() { + return new EditUserDetailsFormBean(); + } + + private ObjectNode orgToJson(Org org) { + ObjectMapper objectMapper = new ObjectMapper(); + if (org == null) { + return objectMapper.createObjectNode(); + } + + ObjectNode jsonOrg = objectMapper.valueToTree(org); + jsonOrg.replace("members", + org.getMembers().stream().map(x -> uncheckedFindAccountByUID(x, objectMapper)).collect( + () -> new ArrayNode(objectMapper.getNodeFactory()), (col, elem) -> col.add(elem), + (col1, col2) -> col1.addAll(col2))); + return jsonOrg; + } + + private ObjectNode uncheckedFindAccountByUID(String uid, ObjectMapper objectMapper) { + Account account; + try { + account = this.accountDao.findByUID(uid); + } catch (Exception e) { + account = new AccountImpl(); + account.setUid(uid); + } + return objectMapper.valueToTree(account); + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/emails/EmailController.java b/console/src/main/java/org/georchestra/console/ws/emails/EmailController.java new file mode 100644 index 0000000000..6afe66cfc1 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/emails/EmailController.java @@ -0,0 +1,586 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.emails; + +import static org.georchestra.commons.security.SecurityHeaders.SEC_EMAIL; +import static org.georchestra.commons.security.SecurityHeaders.SEC_FIRSTNAME; +import static org.georchestra.commons.security.SecurityHeaders.SEC_LASTNAME; +import static org.georchestra.commons.security.SecurityHeaders.SEC_ROLES; +import static org.georchestra.commons.security.SecurityHeaders.SEC_USERNAME; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import javax.activation.DataHandler; +import javax.mail.Address; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.Transport; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MailDateFormat; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; +import javax.mail.util.ByteArrayDataSource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.commons.security.SecurityHeaders; +import org.georchestra.console.dao.AdvancedDelegationDao; +import org.georchestra.console.dao.AttachmentDao; +import org.georchestra.console.dao.EmailDao; +import org.georchestra.console.dao.EmailTemplateDao; +import org.georchestra.console.mailservice.EmailFactory; +import org.georchestra.console.model.AdminLogType; +import org.georchestra.console.model.Attachment; +import org.georchestra.console.model.EmailEntry; +import org.georchestra.console.model.EmailTemplate; +import org.georchestra.console.ws.utils.LogUtils; +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.AccountDao; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class EmailController { + + @Autowired + private EmailDao emailRepository; + + @Autowired + private AttachmentDao attachmentRepo; + + @Autowired + private EmailTemplateDao emailTemplateRepo; + + @Autowired + private AccountDao accountDao; + + @Autowired + private EmailFactory emailFactory; + + @Autowired + private LogUtils logUtils; + + @Autowired + private AdvancedDelegationDao advancedDelegationDao; + + private static final Log LOG = LogFactory.getLog(EmailController.class.getName()); + private Collection recipientWhiteList; + + /** + * Email proxy configuration + * + * Basically, this webapp can send emails on behalf of LDAP users. The service + * endpoint is available at /console/emailProxy Usage is restricted to users + * having the EMAILPROXY role by default, cf + * https://github.com/georchestra/datadir/blob/master/security-proxy/security-mappings.xml + * see https://github.com/georchestra/georchestra/pull/1572 for more + * information. + * + * These restrictions have been implemented to prevent spammers. + */ + @Value("${emailProxyFromAddress:${administratorEmail}}") + private String emailProxyFromAddress; + + @Value("${emailProxyMaxRecipient:10}") + private String emailProxyMaxRecipient; + + @Value("${emailProxyMaxBodySize:10000}") + private String emailProxyMaxBodySize; + + @Value("${emailProxyMaxSubjectSize:200}") + private String emailProxyMaxSubjectSize; + + @Value("${emailProxyRecipientWhitelist:${administratorEmail}}") + private String emailProxyRecipientWhitelist; + + /* + * produces = MediaType.APPLICATION_JSON_VALUE generate : content type : + * application/json; charset=UTF-8 WRONG ! + * + * produces = "application/json; charset=utf-8" generate : content type : + * application/json;charset=utf-8 RIGHT ! + * + */ + + /** + * Return a JSON list of Emails sent to specified user + * + * @param recipient recipient login + * @return JSON list of Emails sent to specified user + * @throws JSONException + */ + @RequestMapping(value = "/{recipient}/emails", method = RequestMethod.GET, produces = "application/json; charset=UTF-8") + @ResponseBody + public String emailsList(@PathVariable String recipient) throws JSONException { + this.checkAuthorisation(recipient); + JSONArray emails = new JSONArray(); + for (EmailEntry email : this.emailRepository.findByRecipientOrderByDateDesc(recipient)) + emails.put(email.toJSON()); + JSONObject res = new JSONObject(); + res.put("emails", emails); + + return res.toString(); + } + + /** + * Send an email and store it in database + * + * @param recipient recipient login + * @param subject subject of email + * @param content content of email (text part in html) + * @param attachmentsIds comma separated list of attachments identifier + * @return identifier of new email sent prefixed by "OK : " + * @throws NameNotFoundException if sender cannot be found + * @throws DataServiceException if there is issues while contacting LDAP server + * @throws MessagingException if email cannot be sent through SMTP protocol + * @throws IOException if email cannot be sent through SMTP protocol + */ + @RequestMapping(value = "{recipient}/sendEmail", method = RequestMethod.POST) + @ResponseBody + public String sendEmail(@PathVariable String recipient, @RequestParam("subject") String subject, + @RequestParam("content") String content, @RequestParam("attachments") String attachmentsIds, + HttpServletRequest request, HttpServletResponse response) + throws NameNotFoundException, DataServiceException, MessagingException, JSONException { + + this.checkAuthorisation(recipient); + + EmailEntry email = new EmailEntry(); + String sender = SecurityHeaders.decode(request.getHeader(SEC_USERNAME)); + email.setSender(sender); + email.setRecipient(recipient); + email.setSubject(subject); + email.setDate(new Date()); + email.setBody(content); + + attachmentsIds = attachmentsIds.trim(); + List attachments = new LinkedList(); + if (attachmentsIds.length() > 0) { + String[] attachmentsIdsList = attachmentsIds.split("\\s?,\\s?"); + for (String attId : attachmentsIdsList) { + Attachment att = this.attachmentRepo.findOne(Long.parseLong(attId)); + if (att == null) + throw new NameNotFoundException("Unable to find attachment with ID : " + attId); + attachments.add(att); + } + } + email.setAttachments(attachments); + this.send(email); + response.setContentType("application/json"); + this.emailRepository.save(email); + + // log that email was sent + String emailString = email.toJSON().toString(); + logUtils.createLog(recipient, AdminLogType.EMAIL_SENT, emailString); + + return emailString; + + } + + /** + * This service can be used to test email sending + * + * @param recipient login of recipient + * @return Html page to test email sending + */ + @RequestMapping(value = "{recipient}/sendEmail", method = RequestMethod.GET) + @ResponseBody + public String sendEmail(@PathVariable String recipient) { + return "
" + "recipient : " + recipient + "
" + + "subject :
" + + "content :
" + + "comma separated list of attachment identifier
" + + "" + "
"; + + } + + /** + * List available attachments in database + * + * @return JSON object containing all attachments available in database + * @throws JSONException if there is error when encoding to JSON + */ + @RequestMapping(value = "/attachments", method = RequestMethod.GET, produces = "application/json; charset=UTF-8") + @ResponseBody + public String attachments() throws JSONException { + JSONArray attachments = new JSONArray(); + for (Attachment att : this.attachmentRepo.findAll()) { + JSONObject attachment = new JSONObject(); + attachment.put("id", att.getId()); + attachment.put("name", att.getName()); + attachment.put("mimeType", att.getMimeType()); + attachments.put(attachment); + } + + JSONObject res = new JSONObject(); + res.put("attachments", attachments); + return res.toString(); + } + + /** + * Generate a list of template found in database + * + * @return JSON object containing all templates found in database + * @throws JSONException if templates cannot be encoded to JSON + */ + @RequestMapping(value = "/emailTemplates", method = RequestMethod.GET, produces = "application/json; charset=UTF-8") + @ResponseBody + public String emailTemplates() throws JSONException { + JSONArray emailTemplates = new JSONArray(); + for (EmailTemplate temp : this.emailTemplateRepo.findAll()) { + JSONObject attachment = new JSONObject(); + attachment.put("id", temp.getId()); + attachment.put("name", temp.getName()); + attachment.put("content", temp.getContent()); + emailTemplates.put(attachment); + } + + JSONObject res = new JSONObject(); + res.put("templates", emailTemplates); + return res.toString(); + } + + /** + * Send an email based on json payload. Recipient should be present in LDAP + * directory or in configured whitelist. + * + * Json sent should have following keys : + * + * - to : json array of email to send email to ex: ["you@rm.fr", + * "another-guy@rm.fr"] - cc : json array of email to 'CC' email ex: + * ["him@rm.fr"] - bcc : json array of email to add recipient as blind CC + * ["secret@rm.fr"] - subject : subject of email - body : Body of email + * + * Either 'to', 'cc' or 'bcc' parameter must be present in request. 'subject' + * and 'body' are mandatory. + * + * complete json example : + * + * { "to": ["you@rm.fr", "another-guy@rm.fr"], "cc": ["him@rm.fr"], "bcc": + * ["secret@rm.fr"], "subject": "test email", "body": "Hi, this a test EMail, + * please do not reply." } + * + */ + @RequestMapping(value = "/emailProxy", method = RequestMethod.POST, produces = "application/json; charset=utf-8", consumes = "application/json") + @ResponseBody + public String emailProxy(@RequestBody String rawRequest, HttpServletRequest request) + throws JSONException, MessagingException, UnsupportedEncodingException, DataServiceException { + + JSONObject payload = new JSONObject(rawRequest); + InternetAddress[] to = this.populateRecipient("to", payload); + InternetAddress[] cc = this.populateRecipient("cc", payload); + InternetAddress[] bcc = this.populateRecipient("bcc", payload); + + this.checkSubject(payload); + this.checkBody(payload); + this.checkRecipient(to, cc, bcc); + + final String secUsername = SecurityHeaders.decode(request.getHeader(SEC_USERNAME)); + final String secRoles = SecurityHeaders.decode(request.getHeader(SEC_ROLES)); + final String secFirstname = SecurityHeaders.decode(request.getHeader(SEC_FIRSTNAME)); + final String secLastName = SecurityHeaders.decode(request.getHeader(SEC_LASTNAME)); + final String secEmail = SecurityHeaders.decode(request.getHeader(SEC_EMAIL)); + + LOG.info("EMail request : user=" + secUsername + " to=" + this.extractAddress("to", payload) + " cc=" + + this.extractAddress("cc", payload) + " bcc=" + this.extractAddress("bcc", payload) + " roles=" + + secRoles); + + LOG.debug("EMail request : " + payload.toString()); + + // Instanciate MimeMessage + MimeMessage message = this.emailFactory.createEmptyMessage(); + + // Generate From header + InternetAddress from = new InternetAddress(); + from.setAddress(emailProxyFromAddress); + from.setPersonal(secFirstname + " " + secLastName); + message.setFrom(from); + + // Generate Reply-to header + InternetAddress replyTo = new InternetAddress(); + replyTo.setAddress(secEmail); + replyTo.setPersonal(secFirstname + " " + secLastName); + message.setReplyTo(new Address[] { replyTo }); + + // Generate to, cc and bcc headers + if (to.length > 0) + message.setRecipients(Message.RecipientType.TO, to); + if (cc.length > 0) + message.setRecipients(Message.RecipientType.CC, cc); + if (bcc.length > 0) + message.setRecipients(Message.RecipientType.BCC, bcc); + + // Add subject and body + message.setSubject(payload.getString("subject"), "UTF-8"); + message.setText(payload.getString("body"), "UTF-8", "plain"); + message.setSentDate(new Date()); + + // finally send message + Transport.send(message); + + JSONObject res = new JSONObject(); + res.put("success", true); + return res.toString(); + } + + /** + * Checks 'subject' of request against configuration + * + * @param payload JSONObject to search subject in + */ + private void checkSubject(JSONObject payload) throws JSONException { + + // Checks that subject is present + if (!payload.has("subject") || payload.getString("subject").length() == 0) + throw new JSONException("No subject specified, 'subject' field is required"); + + // Check subject size + if (payload.getString("subject").length() > Integer.parseInt(emailProxyMaxSubjectSize)) + throw new IllegalArgumentException( + "Subject is too long, it should not exceed " + emailProxyMaxSubjectSize + " bytes"); + } + + /** + * Checks 'body' of request against configuration + * + * @param payload JSONObject to search body in + */ + private void checkBody(JSONObject payload) throws JSONException { + + // Checks that body is present + if (!payload.has("body")) + throw new JSONException("No body specified, 'body' field is required"); + + // Check subject and body size + if (payload.getString("body").length() > Integer.parseInt(emailProxyMaxBodySize)) + throw new IllegalArgumentException( + "Body is too long, it should not exceed " + emailProxyMaxBodySize + " bytes"); + + } + + /** + * Checks recipients of request against configuration + * + * @param to array of recipients for 'to' field + * @param cc array of recipients for 'cc' field + * @param bcc array of recipients for 'bcc' field + */ + private void checkRecipient(InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc) + throws JSONException, DataServiceException { + + if (to.length == 0 && cc.length == 0 && bcc.length == 0) + throw new JSONException("One of 'to', 'cc' or 'bcc' must be present in request"); + + // Check recipient count against proxyMaxRecipient + if ((to.length + cc.length + bcc.length) > Integer.parseInt(emailProxyMaxRecipient)) + throw new IllegalArgumentException( + "Too many recipient in request, max recipient : " + emailProxyMaxRecipient); + + // Check Recipients validity + for (int i = 0; i < to.length; i++) + if (!this.recipientIsAllowed(to[i].getAddress())) + throw new IllegalArgumentException("Recipient not allowed : " + to[i].getAddress()); + for (int i = 0; i < cc.length; i++) + if (!this.recipientIsAllowed(cc[i].getAddress())) + throw new IllegalArgumentException("Recipient not allowed : " + cc[i].getAddress()); + for (int i = 0; i < bcc.length; i++) + if (!this.recipientIsAllowed(bcc[i].getAddress())) + throw new IllegalArgumentException("Recipient not allowed : " + bcc[i].getAddress()); + + } + + /** + * Check if recipient is under delegation for delegated admins + * + * @param recipient + * @throws AccessDeniedException if current does not have permissions on + * recipient + */ + private void checkAuthorisation(String recipient) { + // check if recipient is under delegation for delegated admins + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (!auth.getAuthorities().contains(this.advancedDelegationDao.ROLE_SUPERUSER)) + if (!this.advancedDelegationDao.findUsersUnderDelegation(auth.getName()).contains(recipient)) + throw new AccessDeniedException("User " + recipient + " not under delegation"); + } + + /** + * Return EMail addresses in JSON array to a String + * + * @param field field name under which EMails are store in payload + * @param payload JsonObject to search EMails addresses + * @return + * @throws JSONException + */ + private String extractAddress(String field, JSONObject payload) throws JSONException { + StringBuilder res = new StringBuilder(); + if (payload.has(field)) { + JSONArray rawTo = payload.getJSONArray(field); + for (int i = 0; i < rawTo.length(); i++) { + if (i > 0) + res.append(","); + res.append(rawTo.getString(i)); + } + } + return res.toString(); + } + + /** + * Create an java String list based on json array found in json ojbect + * + * @param field field name where to find json array to parse + * @param request full object where to search for key + * @return java list of extracted values + */ + private InternetAddress[] populateRecipient(String field, JSONObject request) + throws JSONException, AddressException { + List res = new LinkedList(); + if (request.has(field)) { + JSONArray rawTo = request.getJSONArray(field); + for (int i = 0; i < rawTo.length(); i++) { + InternetAddress to = new InternetAddress(); + to.setAddress(rawTo.getString(i)); + to.validate(); + res.add(to); + } + } + return res.toArray(new InternetAddress[res.size()]); + } + + private boolean recipientIsAllowed(String recipient) throws DataServiceException { + // Load configuration if not already loaded + if (this.recipientWhiteList == null) + this.recipientWhiteList = Arrays.asList(emailProxyRecipientWhitelist.split("\\s*,\\s*")); + + // Check recipient in whitelist + if (this.recipientWhiteList.contains(recipient)) + return true; + + // Check recipient in LDAP and against delegation for delegated admins + try { + Account account = this.accountDao.findByEmail(recipient); + if (account == null) + return false; + this.checkAuthorisation(account.getUid()); + return true; + } catch (NameNotFoundException ex) { + return false; + } catch (AccessDeniedException ex) { + return false; + } + } + + /** + * Send EmailEntry to smtp server + * + * @param email email to send + * @throws NameNotFoundException if recipient cannot be found in LDAP server + * @throws DataServiceException if LDAP server is not available + * @throws MessagingException if some field of email cannot be encoded + * (malformed email address) + */ + private void send(EmailEntry email) throws NameNotFoundException, DataServiceException, MessagingException { + + final MimeMessage message = this.emailFactory.createEmptyMessage(); + + Account recipient = this.accountDao.findByUID(email.getRecipient()); + InternetAddress[] senders = { new InternetAddress(this.accountDao.findByUID(email.getSender()).getEmail()) }; + + message.addFrom(senders); + message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient.getEmail())); + message.setSubject(email.getSubject()); + message.setHeader("Date", (new MailDateFormat()).format(email.getDate())); + + // Mail content + Multipart multiPart = new MimeMultipart("alternative"); + + // attachments + for (Attachment att : email.getAttachments()) { + MimeBodyPart mbp = new MimeBodyPart(); + mbp.setDataHandler(new DataHandler(new ByteArrayDataSource(att.getContent(), att.getMimeType()))); + mbp.setFileName(att.getName()); + multiPart.addBodyPart(mbp); + } + + // html part + MimeBodyPart htmlPart = new MimeBodyPart(); + htmlPart.setContent(email.getBody(), "text/html; charset=utf-8"); + multiPart.addBodyPart(htmlPart); + + message.setContent(multiPart); + + // Send message + Transport.send(message); + } + + // Getter for unit tests + public AccountDao getAccountDao() { + return accountDao; + } + + public void setAccountDao(AccountDao accountDao) { + this.accountDao = accountDao; + } + + public void setEmailProxyFromAddress(String emailProxyFromAddress) { + this.emailProxyFromAddress = emailProxyFromAddress; + } + + public void setEmailProxyMaxRecipient(String emailProxyMaxRecipient) { + this.emailProxyMaxRecipient = emailProxyMaxRecipient; + } + + public void setEmailProxyMaxBodySize(String emailProxyMaxBodySize) { + this.emailProxyMaxBodySize = emailProxyMaxBodySize; + } + + public void setEmailProxyMaxSubjectSize(String emailProxyMaxSubjectSize) { + this.emailProxyMaxSubjectSize = emailProxyMaxSubjectSize; + } + + public void setEmailProxyRecipientWhitelist(String emailProxyRecipientWhitelist) { + this.emailProxyRecipientWhitelist = emailProxyRecipientWhitelist; + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/emails/package-info.java b/console/src/main/java/org/georchestra/console/ws/emails/package-info.java new file mode 100644 index 0000000000..49e35bd27e --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/emails/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +/** + * This package holds information about emails sent from ldapdamin UI + * + * Email can contain attachments stored in DB + */ +package org.georchestra.console.ws.emails; \ No newline at end of file diff --git a/console/src/main/java/org/georchestra/console/ws/newaccount/AccountFormBean.java b/console/src/main/java/org/georchestra/console/ws/newaccount/AccountFormBean.java new file mode 100644 index 0000000000..ffb7ff6415 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/newaccount/AccountFormBean.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.newaccount; + +import java.io.Serializable; + +/** + * This model maintains the account form data. + * + * @author Mauricio Pazos + * + */ +public class AccountFormBean implements Serializable { + + private static final long serialVersionUID = 6955470190631684934L; + + private String uid; + private String firstName; + private String surname; + + private String org; + private String title; + private String email; + private String phone; + private String description; + private String password; + private String confirmPassword; + private boolean privacyPolicyAgreed; + + private String recaptcha_response_field; + + // Org creation fields + private boolean createOrg; + private String orgName; + private String orgShortName; + private String orgAddress; + private String orgType; + private String orgDescription; + private String orgUrl; + private String orgLogo; + + public String getRecaptcha_response_field() { + return recaptcha_response_field; + } + + public void setRecaptcha_response_field(String recaptcha_response_field) { + this.recaptcha_response_field = recaptcha_response_field; + } + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String name) { + this.firstName = name; + } + + public String getOrg() { + return org; + } + + public void setOrg(String org) { + this.org = org; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + public boolean getPrivacyPolicyAgreed() { + return privacyPolicyAgreed; + } + + public void setPrivacyPolicyAgreed(boolean privacyPolicyAgreed) { + this.privacyPolicyAgreed = privacyPolicyAgreed; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String sn) { + this.surname = sn; + } + + public boolean getCreateOrg() { + return createOrg; + } + + public void setCreateOrg(boolean createOrg) { + this.createOrg = createOrg; + } + + public String getOrgName() { + return orgName; + } + + public void setOrgName(String orgName) { + this.orgName = orgName; + } + + public String getOrgShortName() { + return orgShortName; + } + + public void setOrgShortName(String orgShortName) { + this.orgShortName = orgShortName; + } + + public String getOrgAddress() { + return orgAddress; + } + + public void setOrgAddress(String orgAddress) { + this.orgAddress = orgAddress; + } + + public String getOrgType() { + return orgType; + } + + public void setOrgType(String orgType) { + this.orgType = orgType; + } + + public String getOrgDescription() { + return orgDescription; + } + + public void setOrgDescription(String orgDescription) { + this.orgDescription = orgDescription; + } + + public String getOrgUrl() { + return orgUrl; + } + + public void setOrgUrl(String orgUrl) { + this.orgUrl = orgUrl; + } + + public String getOrgLogo() { + return orgLogo; + } + + public void setOrgLogo(String orgLogo) { + this.orgLogo = orgLogo; + } + + @Override + public String toString() { + return "AccountFormBean [uid=" + uid + ", firstName=" + firstName + ", surname=" + surname + ", org=" + org + + ", title=" + title + ", email=" + email + ", phone=" + phone + ", description=" + description + + ", password=" + password + ", confirmPassword=" + confirmPassword + ", privacyPolicyAgreed=" + + privacyPolicyAgreed + ", recaptcha_response_field=" + recaptcha_response_field + "]"; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/newaccount/NewAccountFormController.java b/console/src/main/java/org/georchestra/console/ws/newaccount/NewAccountFormController.java new file mode 100644 index 0000000000..3c688d19b6 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/newaccount/NewAccountFormController.java @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.newaccount; + +import java.io.IOException; +import java.sql.SQLException; +import java.time.Clock; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.mail.MessagingException; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.validator.routines.EmailValidator; +import org.georchestra.console.bs.ReCaptchaParameters; +import org.georchestra.console.dao.AdvancedDelegationDao; +import org.georchestra.console.mailservice.EmailFactory; +import org.georchestra.console.model.AdminLogType; +import org.georchestra.console.model.DelegationEntry; +import org.georchestra.console.ws.utils.LogUtils; +import org.georchestra.console.ws.utils.PasswordUtils; +import org.georchestra.console.ws.utils.RecaptchaUtils; +import org.georchestra.console.ws.utils.Validation; +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.orgs.Org; +import org.georchestra.ds.orgs.OrgsDao; +import org.georchestra.ds.roles.Role; +import org.georchestra.ds.roles.RoleDao; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.AccountDao; +import org.georchestra.ds.users.AccountFactory; +import org.georchestra.ds.users.DuplicatedEmailException; +import org.georchestra.ds.users.DuplicatedUidException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.bind.support.SessionStatus; + +/** + * Manages the UI Account Form. + * + * @author Mauricio Pazos + * + */ +@Controller +@SessionAttributes(types = { AccountFormBean.class }) +public final class NewAccountFormController { + + private static final Log LOG = LogFactory.getLog(NewAccountFormController.class.getName()); + + @Autowired + private AccountDao accountDao; + + @Autowired + private OrgsDao orgDao; + + @Autowired + private RoleDao roleDao; + + @Autowired + private EmailFactory emailFactory; + + @Autowired + private AdvancedDelegationDao advancedDelegationDao; + + @Autowired + protected PasswordUtils passwordUtils; + + @Autowired + private boolean moderatedSignup = true; + + @Autowired + protected boolean reCaptchaActivated; + private ReCaptchaParameters reCaptchaParameters; + + @Autowired + protected boolean privacyPolicyAgreementActivated; + + @Autowired + protected String privacyPolicyAgreementUrl; + + @Autowired + protected LogUtils logUtils; + + @Autowired + protected Clock clock; + + private Validation validation; + + @Autowired + public NewAccountFormController(ReCaptchaParameters reCaptchaParameters, Validation validation) { + this.reCaptchaParameters = reCaptchaParameters; + this.validation = validation; + } + + public void setAccountDao(AccountDao accountDao) { + this.accountDao = accountDao; + } + + public void setOrgDao(OrgsDao orgDao) { + this.orgDao = orgDao; + } + + public void setEmailFactory(EmailFactory emailFactory) { + this.emailFactory = emailFactory; + } + + public void setAdvancedDelegationDao(AdvancedDelegationDao advancedDelegationDao) { + this.advancedDelegationDao = advancedDelegationDao; + } + + public void setModeratedSignup(boolean moderatedSignup) { + this.moderatedSignup = moderatedSignup; + } + + public void setRoleDao(RoleDao roleDao) { + this.roleDao = roleDao; + } + + @ModelAttribute("accountFormBean") + public AccountFormBean getAccountFormBean() { + return new AccountFormBean(); + } + + @InitBinder + public void initForm(WebDataBinder dataBinder) { + dataBinder.setAllowedFields("firstName", "surname", "email", "phone", "org", "title", "description", "uid", + "password", "confirmPassword", "privacyPolicyAgreed", "createOrg", "orgName", "orgShortName", + "orgAddress", "orgType", "orgCities", "orgDescription", "orgUrl", "orgLogo", + "recaptcha_response_field"); + } + + @RequestMapping(value = "/account/new", method = RequestMethod.GET) + public String setupForm(HttpServletRequest request, Model model) throws IOException { + + HttpSession session = request.getSession(); + + populateOrgsAndOrgTypes(model); + + model.addAttribute("privacyPolicyAgreementActivated", this.privacyPolicyAgreementActivated); + model.addAttribute("privacyPolicyAgreementUrl", this.privacyPolicyAgreementUrl); + + model.addAttribute("recaptchaActivated", this.reCaptchaActivated); + + session.setAttribute("reCaptchaPublicKey", reCaptchaParameters.getPublicKey()); + for (String f : validation.getRequiredUserFields()) { + session.setAttribute(f + "Required", "true"); + } + // Convert to camelcase with 'org' prefix 'shortName' --> 'orgShortName' + for (String f : validation.getRequiredOrgFields()) { + session.setAttribute("org" + f.substring(0, 1).toUpperCase() + f.substring(1, f.length()) + "Required", + "true"); + } + + return "createAccountForm"; + } + + /** + * Creates a new account in ldap. If the application was configured with + * "moderated signup" the new account is added inside "ou=pendingusers" LDAP + * organizational unit, in the other case, it's inserted in the "ou=users" + * organization unit. + * + * + * @param formBean + * @param result + * @param sessionStatus + * + * @return the next view + * + * @throws IOException + */ + @RequestMapping(value = "/account/new", method = RequestMethod.POST) + public String create(HttpServletRequest request, @ModelAttribute AccountFormBean formBean, + @RequestParam("orgCities") String orgCities, BindingResult result, SessionStatus sessionStatus, Model model) + throws IOException, SQLException { + + populateOrgsAndOrgTypes(model); + + validateFields(formBean, result); + + if (result.hasErrors()) { + return "createAccountForm"; + } + + if (formBean.getCreateOrg()) { + try { + Org org = new Org(); + + // Generate textual identifier based on name + String orgId = orgDao.generateId(formBean.getOrgShortName()); + org.setId(orgId); + + // Store name, short name, orgType and address + org.setName(formBean.getOrgName()); + org.setShortName(formBean.getOrgShortName()); + org.setAddress(formBean.getOrgAddress()); + org.setOrgType(formBean.getOrgType()); + org.setDescription(formBean.getOrgDescription()); + org.setUrl(formBean.getOrgUrl()); + org.setLogo(formBean.getOrgLogo()); + // Parse and store cities + orgCities = orgCities.trim(); + if (orgCities.length() > 0) + org.setCities(Arrays.asList(orgCities.split("\\s*,\\s*"))); + + org.setPending(this.moderatedSignup); + // Persist changes to LDAP server + orgDao.insert(org); + + // Set real org identifier in form + formBean.setOrg(orgId); + + // log - new pending org was created + if (org.isPending()) { + logUtils.createLog(orgId, AdminLogType.PENDING_ORG_CREATED, null); + } + } catch (Exception e) { + LOG.error(e.getMessage()); + throw new IOException(e); + } + } + + // inserts the new account + try { + + Account account = AccountFactory.createBrief(formBean.getUid().toLowerCase(), formBean.getPassword(), + formBean.getFirstName(), formBean.getSurname(), formBean.getEmail(), formBean.getPhone(), + formBean.getTitle(), formBean.getDescription()); + + if (!formBean.getOrg().equals("-")) + account.setOrg(formBean.getOrg()); + + account.setPending(this.moderatedSignup); + + if (privacyPolicyAgreementActivated) { + account.setPrivacyPolicyAgreementDate(LocalDate.now(clock)); + } + + accountDao.insert(account); + roleDao.addUser(Role.USER, account); + orgDao.linkUser(account); + + final ServletContext servletContext = request.getSession().getServletContext(); + + // List of recipients for notification email + List recipients = accountDao.findByRole("SUPERUSER").stream().map(Account::getEmail) + .collect(Collectors.toCollection(LinkedList::new)); + + // Retrieve emails of delegated admin if org is specified + if (!formBean.getOrg().equals("-")) { + // and a delegation is defined + List delegations = advancedDelegationDao.findByOrg(formBean.getOrg()); + + for (DelegationEntry delegation : delegations) { + Account delegatedAdmin = accountDao.findByUID(delegation.getUid()); + recipients.add(delegatedAdmin.getEmail()); + } + } + + // Select email template based on moderation configuration for admin and user + // and send emails + String orgName = orgDao.findByCommonName(account.getOrg()).getName(); + if (this.moderatedSignup) { + emailFactory.sendNewAccountRequiresModerationEmail(servletContext, recipients, account.getCommonName(), + account.getUid(), account.getEmail(), orgName); + emailFactory.sendAccountCreationInProcessEmail(servletContext, account.getEmail(), + account.getCommonName(), account.getUid()); + } else { + emailFactory.sendNewAccountNotificationEmail(servletContext, recipients, account.getCommonName(), + account.getUid(), account.getEmail(), orgName); + emailFactory.sendAccountWasCreatedEmail(servletContext, account.getEmail(), account.getCommonName(), + account.getUid()); + } + sessionStatus.setComplete(); + + // log - new pending user was created + if (account.isPending()) { + logUtils.createLog(account.getUid(), AdminLogType.PENDING_USER_CREATED, null); + } + + return "welcomeNewUser"; + + } catch (DuplicatedEmailException e) { + + result.rejectValue("email", "email.error.exist", "there is a user with this e-mail"); + return "createAccountForm"; + + } catch (DuplicatedUidException e) { + + formBean.setUid(accountDao.generateUid(formBean.getUid())); + result.rejectValue("uid", "uid.error.exist", "the uid exist"); + return "createAccountForm"; + + } catch (DataServiceException | MessagingException e) { + + throw new IOException(e); + } + } + + private void validateFields(@ModelAttribute AccountFormBean formBean, BindingResult result) { + // uid validation + if (validation.validateUserFieldWithSpecificMsg("uid", formBean.getUid(), result)) { + // A valid user identifier (uid) can only contain characters, numbers, hyphens + // or dot. + // It must begin with a character. + // keep in sync with the regexp in + // webapp/manager/app/templates/userForm.tpl.html + Pattern regexp = Pattern.compile("[a-zA-Z][a-zA-Z0-9_\\.\\-]*"); + Matcher m = regexp.matcher(formBean.getUid()); + if (!m.matches()) + result.rejectValue("uid", "uid.error.invalid", "required"); + } + + // first name and surname validation + validation.validateUserFieldWithSpecificMsg("firstName", formBean.getFirstName(), result); + validation.validateUserFieldWithSpecificMsg("surname", formBean.getSurname(), result); + + // email validation + if (validation.validateUserFieldWithSpecificMsg("email", formBean.getEmail(), result) + && !EmailValidator.getInstance().isValid(formBean.getEmail())) { + result.rejectValue("email", "email.error.invalidFormat", "Invalid Format"); + } + + // password validation + passwordUtils.validate(formBean.getPassword(), formBean.getConfirmPassword(), result); + + // Check captcha + if (reCaptchaActivated) { + RecaptchaUtils.validate(reCaptchaParameters, formBean.getRecaptcha_response_field(), result); + } + + // Check if the user has agreed to the privacy policy + if (privacyPolicyAgreementActivated) { + validation.validatePrivacyPolicyAgreedField(formBean.getPrivacyPolicyAgreed(), result); + } + + // Validate remaining fields + validation.validateUserField("phone", formBean.getPhone(), result); + validation.validateUserField("title", formBean.getTitle(), result); + validation.validateUserField("description", formBean.getDescription(), result); + + if (formBean.getCreateOrg() && !result.hasErrors()) { + validation.validateOrgField("name", formBean.getOrgName(), result); + validation.validateOrgField("shortName", formBean.getOrgShortName(), result); + validation.validateOrgField("address", formBean.getOrgAddress(), result); + validation.validateOrgField("type", formBean.getOrgType(), result); + validation.validateOrgField("url", formBean.getOrgUrl(), result); + validation.validateOrgField("description", formBean.getOrgDescription(), result); + validation.validateOrgField("logo", formBean.getOrgLogo(), result); + validation.validateUrlFieldWithSpecificMsg("orgUrl", formBean.getOrgUrl(), result); + } else { + validation.validateUserField("org", formBean.getOrg(), result); + } + } + + private void populateOrgsAndOrgTypes(Model model) { + model.addAttribute("orgs", getOrgs()); + model.addAttribute("orgTypes", getOrgTypes()); + } + + private Map getOrgTypes() { + return Arrays.stream(orgDao.getOrgTypeValues()) + .collect(Collectors.toMap(Function.identity(), Function.identity())); + } + + /** + * Create a sorted Map of organization sorted by human readable name + */ + private Map getOrgs() { + return orgDao.findValidated().stream().sorted((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())) + .collect(Collectors.toMap(Org::getId, Org::getName, (oldValue, newValue) -> oldValue, + LinkedHashMap::new)); + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/passwordrecovery/NewPasswordFormBean.java b/console/src/main/java/org/georchestra/console/ws/passwordrecovery/NewPasswordFormBean.java new file mode 100644 index 0000000000..1f4adab7b8 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/passwordrecovery/NewPasswordFormBean.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.passwordrecovery; + +import java.io.Serializable; + +/** + * Maintains the new password typed by the user. + * + * @author Mauricio Pazos + */ +public class NewPasswordFormBean implements Serializable { + + private static final long serialVersionUID = 3239632432961416372L; + + private String uid; + private String token; + private String password; + private String confirmPassword; + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + @Override + public String toString() { + return "NewPasswordFormBean [uid=" + uid + ", token=" + token + ", password=" + password + ", confirmPassword=" + + confirmPassword + "]"; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/passwordrecovery/NewPasswordFormController.java b/console/src/main/java/org/georchestra/console/ws/passwordrecovery/NewPasswordFormController.java new file mode 100644 index 0000000000..2370f619de --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/passwordrecovery/NewPasswordFormController.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.passwordrecovery; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.console.ds.UserTokenDao; +import org.georchestra.console.ws.utils.PasswordUtils; +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.users.AccountDao; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.bind.support.SessionStatus; + +/** + * This controller implements the interactions required to ask for a new + * password based on a token provide. + * + * @author Mauricio Pazos + * + */ +@Controller +@SessionAttributes(types = NewPasswordFormBean.class) +public class NewPasswordFormController { + + private static final Log LOG = LogFactory.getLog(NewPasswordFormController.class.getName()); + + @Autowired + protected PasswordUtils passwordUtils; + + private final AccountDao accountDao; + private final UserTokenDao userTokenDao; + + @Autowired + private boolean reCaptchaActivated; + + @Autowired + public NewPasswordFormController(AccountDao accountDao, UserTokenDao userTokenDao) { + this.accountDao = accountDao; + this.userTokenDao = userTokenDao; + } + + @InitBinder + public void initForm(WebDataBinder dataBinder) { + dataBinder.setAllowedFields("password", "confirmPassword"); + } + + /** + * Search the user associated to the provided token, then initialize the + * {@link NewPasswordFormBean}. If the token is not valid (it didn't exist in + * the system registry) the PasswordRecoveryForm is presented to offer a new + * chance to the user. + * + * @param token the token was generated by the + * {@link PasswordRecoveryFormController}} + * @param model + * + * @return newPasswordForm or passwordRecoveryForm + * + * @throws IOException + */ + @RequestMapping(value = "/account/newPassword", method = RequestMethod.GET) + public String setupForm(@RequestParam("token") String token, Model model) throws IOException { + + model.addAttribute("recaptchaActivated", reCaptchaActivated); + + try { + final String uid = this.userTokenDao.findUserByToken(token); + + NewPasswordFormBean formBean = new NewPasswordFormBean(); + + formBean.setToken(token); + formBean.setUid(uid); + + model.addAttribute(formBean); + + return "newPasswordForm"; + + } catch (NameNotFoundException e) { + + return "passwordRecoveryForm"; + + } catch (DataServiceException e) { + + LOG.error("cannot insert the setup the passwordRecoveryForm. " + e.getMessage()); + + throw new IOException(e); + } + + } + + /** + * Registers the new password, if it is valid. + * + * @param formBean + * @param result + * @param sessionStatus + * + * @return the next view + * + * @throws IOException + */ + @RequestMapping(value = "/account/newPassword", method = RequestMethod.POST) + public String newPassword(@ModelAttribute NewPasswordFormBean formBean, BindingResult result, + SessionStatus sessionStatus) throws IOException { + + passwordUtils.validate(formBean.getPassword(), formBean.getConfirmPassword(), result); + + if (result.hasErrors()) { + + return "newPasswordForm"; + } + + // changes the user's password and removes the token + try { + + String uid = formBean.getUid(); + String password = formBean.getPassword(); + + this.accountDao.changePassword(uid, password); + + this.userTokenDao.delete(uid); + + sessionStatus.setComplete(); + + return "passwordUpdated"; + + } catch (DataServiceException e) { + LOG.error("cannot set the the new password. " + e.getMessage()); + + throw new IOException(e); + + } + } + + @ModelAttribute("newPasswordFormBean") + public NewPasswordFormBean getNewPasswordFormBean() { + return new NewPasswordFormBean(); + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/passwordrecovery/PasswordRecoveryFormBean.java b/console/src/main/java/org/georchestra/console/ws/passwordrecovery/PasswordRecoveryFormBean.java new file mode 100644 index 0000000000..671dc84a56 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/passwordrecovery/PasswordRecoveryFormBean.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.passwordrecovery; + +import java.io.Serializable; + +public class PasswordRecoveryFormBean implements Serializable { + + /** + * + */ + private static final long serialVersionUID = 7773803527246666406L; + + private String email; + private String recaptcha_response_field; + + @Override + public String toString() { + return "PasswordRecoveryFormBean [email=" + email + ", recaptcha_response_field=" + recaptcha_response_field + + "]"; + } + + public String getEmail() { + return this.email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getRecaptcha_response_field() { + return recaptcha_response_field; + } + + public void setRecaptcha_response_field(String recaptcha_response_field) { + this.recaptcha_response_field = recaptcha_response_field; + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/passwordrecovery/PasswordRecoveryFormController.java b/console/src/main/java/org/georchestra/console/ws/passwordrecovery/PasswordRecoveryFormController.java new file mode 100644 index 0000000000..5e8ee8a756 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/passwordrecovery/PasswordRecoveryFormController.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.passwordrecovery; + +import java.io.IOException; +import java.util.UUID; + +import javax.mail.MessagingException; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.console.bs.ReCaptchaParameters; +import org.georchestra.console.ds.UserTokenDao; +import org.georchestra.console.mailservice.EmailFactory; +import org.georchestra.console.model.AdminLogType; +import org.georchestra.console.ws.utils.LogUtils; +import org.georchestra.console.ws.utils.RecaptchaUtils; +import org.georchestra.ds.DataServiceException; +import org.georchestra.ds.roles.RoleDao; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.AccountDao; +import org.georchestra.ds.users.PasswordType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.bind.support.SessionStatus; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Manage the user interactions required to implement the lost password + * workflow: + *

+ *

    + * + *
  • Present a form in order to ask for the user's mail.
  • + * + *
  • If the given email matches one of the LDAP users, an email is sent to + * this user with a unique http URL to reset his password.
  • + * + *
  • As result of this interaction the view EmailSentForm.jsp is + * presented
  • + *
+ *

+ * + * @author Mauricio Pazos + */ +@Controller +@SessionAttributes(types = PasswordRecoveryFormBean.class) +public class PasswordRecoveryFormController { + + protected static final Log LOG = LogFactory.getLog(PasswordRecoveryFormController.class.getName()); + + // collaborations + private final AccountDao accountDao; + private final RoleDao roleDao; + private EmailFactory emailFactory; + private final UserTokenDao userTokenDao; + private final ReCaptchaParameters reCaptchaParameters; + + @Autowired + private boolean reCaptchaActivated; + + @Autowired + protected LogUtils logUtils; + + @Value("${publicContextPath:/console}") + private String publicContextPath; + + @Value("${scheme}://${domainName}") + private String publicUrl; + + @Autowired + public PasswordRecoveryFormController(AccountDao dao, RoleDao gDao, EmailFactory emailFactory, + UserTokenDao userTokenDao, ReCaptchaParameters reCaptchaParameters) { + this.accountDao = dao; + this.roleDao = gDao; + this.emailFactory = emailFactory; + this.userTokenDao = userTokenDao; + this.reCaptchaParameters = reCaptchaParameters; + } + + @InitBinder + public void initForm(WebDataBinder dataBinder) { + + dataBinder.setAllowedFields("email", "recaptcha_response_field"); + } + + @RequestMapping(value = "/account/passwordRecovery", method = RequestMethod.GET) + public String setupForm(HttpServletRequest request, @RequestParam(value = "email", required = false) String email, + Model model) throws DataServiceException { + + if (email != null) { + PasswordType pt = getPasswordType(email); + if (pt == PasswordType.SASL) { + return "userManagedBySASL"; + } + } + + HttpSession session = request.getSession(); + PasswordRecoveryFormBean formBean = new PasswordRecoveryFormBean(); + formBean.setEmail(email); + + model.addAttribute(formBean); + model.addAttribute("recaptchaActivated", this.reCaptchaActivated); + session.setAttribute("reCaptchaPublicKey", this.reCaptchaParameters.getPublicKey()); + + return "passwordRecoveryForm"; + } + + /** + * Generates a new unique http URL based on a token, then an e-mail is sent to + * the user with instruction to change his password. + * + * + * @param formBean Contains the user's email + * @param resultErrors will be updated with the list of found errors. + * @param sessionStatus + * + * @return the next view + * + * @throws IOException + */ + @RequestMapping(value = "/account/passwordRecovery", method = RequestMethod.POST) + public String generateToken(HttpServletRequest request, @ModelAttribute PasswordRecoveryFormBean formBean, + BindingResult resultErrors, SessionStatus sessionStatus) throws IOException { + + if (reCaptchaActivated) { + RecaptchaUtils.validate(reCaptchaParameters, formBean.getRecaptcha_response_field(), resultErrors); + } + if (resultErrors.hasErrors()) { + return "passwordRecoveryForm"; + } + + try { + Account account = this.accountDao.findByEmail(formBean.getEmail()); + // Finds the user using the email as key, if it exists a new token is generated + // to include in the unique http URL. + + if (account.isPending()) { + throw new NameNotFoundException("User is pending"); + } + + String token = UUID.randomUUID().toString(); + + // if there is a previous token it is removed + if (this.userTokenDao.exist(account.getUid())) { + this.userTokenDao.delete(account.getUid()); + } + + this.userTokenDao.insertToken(account.getUid(), token); + + String url = makeChangePasswordURL(publicUrl, publicContextPath, token); + + ServletContext servletContext = request.getSession().getServletContext(); + this.emailFactory.sendChangePasswordEmail(servletContext, account.getEmail(), account.getCommonName(), + account.getUid(), url); + sessionStatus.setComplete(); + + // log role deleted + logUtils.createLog(account.getUid(), AdminLogType.EMAIL_RECOVERY_SENT, ""); + + return "emailWasSent"; + + } catch (DataServiceException | MessagingException e) { + throw new IOException(e); + } catch (NameNotFoundException e) { + resultErrors.rejectValue("email", "email.error.notFound", "No user found for this email."); + return "passwordRecoveryForm"; + } + } + + /** + * Create the URL to change the password based on the provided token + * + * @return a new URL to change password + */ + protected String makeChangePasswordURL(final String publicUrl, final String contextPath, final String token) { + String url = UriComponentsBuilder.fromHttpUrl(publicUrl).path(contextPath).path("/account/newPassword") + .query("token={token}").buildAndExpand(token).toUriString(); + + if (LOG.isDebugEnabled()) { + LOG.debug("generated url:" + url); + } + + return url; + } + + @ModelAttribute("passwordRecoveryFormBean") + public PasswordRecoveryFormBean getPasswordRecoveryFormBean() { + return new PasswordRecoveryFormBean(); + } + + public void setEmailFactory(EmailFactory emailFactory) { + this.emailFactory = emailFactory; + } + + public void setPublicUrl(String publicUrl) { + this.publicUrl = publicUrl; + } + + public void setPublicContextPath(String publicContextPath) { + this.publicContextPath = publicContextPath; + } + + private PasswordType getPasswordType(String email) throws DataServiceException { + Account a = this.accountDao.findByEmail(email); + return a.getPasswordType(); + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/security/api/SecurityApiController.java b/console/src/main/java/org/georchestra/console/ws/security/api/SecurityApiController.java new file mode 100644 index 0000000000..ce1fb69dfd --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/security/api/SecurityApiController.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ +package org.georchestra.console.ws.security.api; + +import java.util.List; +import java.util.Optional; + +import org.georchestra.security.api.OrganizationsApi; +import org.georchestra.security.api.RolesApi; +import org.georchestra.security.api.UsersApi; +import org.georchestra.security.model.GeorchestraUser; +import org.georchestra.security.model.Organization; +import org.georchestra.security.model.Role; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(value = "/internal", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) +public class SecurityApiController { + + private @Autowired RolesApi roles; + private @Autowired OrganizationsApi orgs; + private @Autowired UsersApi users; + + /** + * Return a list of available users as a json array. + *

+ * This is the server-side counterpart of {@link UsersApi#findAll} + */ + @GetMapping(value = "/users") + @ResponseBody + public List findUsers() { + return users.findAll(); + } + + /** + * Return a user by {@link GeorchestraUser#getId() id} + *

+ * This is the server-side counterpart of {@link UsersApi#findById} + */ + @GetMapping(value = "/users/id/{id}") + public ResponseEntity findUserById(@PathVariable("id") String id) { + return toEntityOrNotFound(users.findById(id)); + } + + /** + * Return a user by {@link GeorchestraUser#getUsername() username}. + *

+ * This is the server-side counterpart of {@link UsersApi#findByUsername} + */ + @GetMapping(value = "/users/username/{name}") + public ResponseEntity findUserByUsername(@PathVariable("name") String name) { + return toEntityOrNotFound(users.findByUsername(name)); + } + + /** + * Return a list of available users as a json array + *

+ * This is the server-side counterpart of {@link OrganizationsApi#findAll} + */ + @GetMapping(value = "/organizations") + @ResponseBody + public List findOrganizations() { + return orgs.findAll(); + } + + /** + * Return an organization by {@link Organization#getId() id} + *

+ * This is the server-side counterpart of {@link OrganizationsApi#findById} + */ + @GetMapping(value = "/organizations/id/{id}") + public ResponseEntity findOrganizationById(@PathVariable("id") String id) { + return toEntityOrNotFound(this.orgs.findById(id)); + } + + /** + * Return an organization by {@link Organization#getShortName shortName} + *

+ * This is the server-side counterpart of + * {@link OrganizationsApi#findByShortName} + */ + @GetMapping(value = "/organizations/shortname/{name}") + public ResponseEntity findOrganizationByShortName(@PathVariable("name") String name) { + return toEntityOrNotFound(this.orgs.findByShortName(name)); + } + + /** + * Return a list of available roles as a json array + *

+ * This is the server-side counterpart of {@link RolesApi#findAll} + */ + @GetMapping(value = "/roles") + @ResponseBody + public List findRoles() { + return roles.findAll(); + } + + /** + * Return a Role by its {@link Role#getName() name} + *

+ * This is the server-side counterpart of {@link RolesApi#findByName} + */ + @GetMapping(value = "/roles/name/{name}") + public ResponseEntity findRoleByName(@PathVariable("name") String name) { + return toEntityOrNotFound(roles.findByName(name)); + } + + private ResponseEntity toEntityOrNotFound(Optional found) { + if (found.isPresent()) { + return ResponseEntity.ok(found.get()); + } + return ResponseEntity.notFound().build(); + + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/utils/LogUtils.java b/console/src/main/java/org/georchestra/console/ws/utils/LogUtils.java new file mode 100644 index 0000000000..2a8cfe3b7d --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/utils/LogUtils.java @@ -0,0 +1,382 @@ +package org.georchestra.console.ws.utils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.console.dao.AdminLogDao; +import org.georchestra.console.model.AdminLogEntry; +import org.georchestra.console.model.AdminLogType; +import org.georchestra.ds.orgs.Org; +import org.georchestra.ds.roles.RoleProtected; +import org.georchestra.ds.users.Account; +import org.georchestra.ds.users.UserSchema; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +public class LogUtils { + @Autowired + private AdminLogDao logDao; + + @Autowired + private RoleProtected roles; + + public void setLogDao(AdminLogDao logDao) { + this.logDao = logDao; + } + + private static final Log LOG = LogFactory.getLog(LogUtils.class.getName()); + + /** + * Create log to save and display. + * + * @target String to identify org's + * @type type AdminLogType of log event + * @param values String that represent changed attributes + */ + public AdminLogEntry createLog(String target, AdminLogType type, String values) { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + AdminLogEntry log = new AdminLogEntry(); + + if (auth != null && auth.getName() != null && target != null) { + String admin = auth.getName(); + // case where we don't need to log changes + if (values == null || values.isEmpty()) { + log = new AdminLogEntry(admin, target, type, new Date()); + } else { + log = new AdminLogEntry(admin, target, type, new Date(), values); + } + if (logDao != null) { + try { + logDao.save(log); + } catch (DataIntegrityViolationException divex) { + // Value could be to large for field size + LOG.error("Could not save changed values for admin log, reset value : " + values, divex); + JSONObject errorsjson = new JSONObject(); + errorsjson.put("error", + "Error while inserting admin log in database, see admin log file for more information"); + log.setChanged(errorsjson.toString()); + logDao.save(log); + } + } + } else { + LOG.info("Authentification Security Context is null."); + log = null; + } + return log; + } + + /** + * Return JSONObject from informations to be save as log detail into database. + * This allow to modify details before create log. + * + * @param attributeName String + * @param oldValue String + * @param newValue String + * @param type AdminLogType + * @return JSONObject + */ + public JSONObject getLogDetails(String attributeName, String oldValue, String newValue, AdminLogType type) { + JSONObject details = new JSONObject(); + details.put("field", attributeName != null ? attributeName : ""); + details.put("old", oldValue != null ? oldValue : ""); + details.put("new", newValue != null ? newValue : ""); + details.put("type", type != null ? type.toString() : ""); + return details; + } + + /** + * Full creation by log details creation and log creation directly. + * + * @param target String + * @param attributeName String + * @param oldValue String + * @param newValue String + * @param type AdminLogType that could be any type of log + */ + public void createAndLogDetails(String target, String attributeName, String oldValue, String newValue, + AdminLogType type) { + oldValue = oldValue == null ? "" : oldValue; + newValue = newValue == null ? "" : newValue; + if (oldValue != newValue) { + JSONObject details = getLogDetails(attributeName, oldValue, newValue, type); + createLog(target, type, details.toString()); + } + } + + /** + * Parse a list of accounts to log when a role was added to a user + * + * @param roleId String role uid + * @param users List + * @param admin String as user's uid that realized modification + * @param type AdminLogType that could be any type of log + */ + private void parseUsers(List users, AdminLogType type, JSONObject details) { + if (users != null && !users.isEmpty()) { + for (Account user : users) { + // Add log entry when role was removed + if (type != null && user.getUid() != null && details.length() > 0) { + createLog(user.getUid(), type, details.toString()); + } + } + } + } + + /** + * Parse a list of roles to log when a role was added to a user. This methods + * identify custom and system roles + * + * @param roles String role as uid + * @param users List + * @param admin String as user's uid that realized modification + * @param action boolean to identify if a role was removed (false) or added + * (true) + */ + private void parseRoles(List roles, List users, boolean action) { + AdminLogType type; + if (roles != null && !roles.isEmpty()) { + // parse roles + for (String roleName : roles) { + // log details + JSONObject details = new JSONObject(); + // get log details + if (!this.roles.isProtected(roleName)) { + type = action ? AdminLogType.CUSTOM_ROLE_ADDED : AdminLogType.CUSTOM_ROLE_REMOVED; + } else { + type = action ? AdminLogType.SYSTEM_ROLE_ADDED : AdminLogType.SYSTEM_ROLE_REMOVED; + } + details = action ? getLogDetails(roleName, null, roleName, type) + : getLogDetails(roleName, roleName, null, type); + details.put("isRole", true); + parseUsers(users, type, details); + } + } + } + + /** + * Parse a list of roles to log for each users if a role was added or removed + * + * @param roles String role uid + * @param users List + * @param admin String as user's uid that realized modification + * @param action boolean to identify if a role was removed (false) or added + * (true) + */ + public void logRolesUsersAction(List putRole, List deleteRole, List users) { + // role is added to users + if (putRole != null && !putRole.isEmpty()) { + parseRoles(putRole, users, true); + } else if (deleteRole != null && !deleteRole.isEmpty()) { + // role is removed for users + parseRoles(deleteRole, users, false); + } + } + + /** + * Log org update found in json object. + * + * @param org Org instance to update + * @param json Json document to take information from + * @throws JSONException If something went wrong during information extraction + * from json document + */ + public void logOrgChanged(Org org, JSONObject json) throws IOException { + final int MAX_CITIES = 32; + final String id = json.optString(Org.JSON_ID); + // log name changed + if (!org.getName().equals(json.optString(Org.JSON_NAME))) { + createAndLogDetails(id, Org.JSON_NAME, org.getName(), json.optString(Org.JSON_NAME), + AdminLogType.ORG_ATTRIBUTE_CHANGED); + } + // log short name changed + if (!org.getShortName().equals(json.optString(Org.JSON_SHORT_NAME))) { + createAndLogDetails(json.optString(Org.JSON_SHORT_NAME), Org.JSON_SHORT_NAME, org.getShortName(), + json.optString(Org.JSON_ID), AdminLogType.ORG_ATTRIBUTE_CHANGED); + } + + // get area differences between old and new list + // Make sure Cities exist + int rmLen = 0; + int addLen = 0; + List removed = new ArrayList<>(); + List added = new ArrayList<>(); + + if (org.getCities() != null && json.optJSONArray(Org.JSON_CITIES) != null) { + removed = org.getCities().stream().filter(p -> !json.optJSONArray(Org.JSON_CITIES).toList().contains(p)) + .collect(Collectors.toList()); + rmLen = removed.size(); + added = json.optJSONArray(Org.JSON_CITIES).toList().stream().filter(p -> !org.getCities().contains(p)) + .collect(Collectors.toList()); + addLen = added.size(); + } + + // create log + if (rmLen > 0 || addLen > 0) { + String oldCities = rmLen > 0 && rmLen < MAX_CITIES ? removed.toString() : ""; + String newCities = addLen > 0 && addLen < MAX_CITIES ? added.toString() : ""; + JSONObject details = getLogDetails(Org.JSON_CITIES, oldCities, newCities, + AdminLogType.ORG_ATTRIBUTE_CHANGED); + details.put("added", addLen); + details.put("removed", rmLen); + createLog(id, AdminLogType.ORG_ATTRIBUTE_CHANGED, details.toString()); + } + + AdminLogType type = AdminLogType.ORG_ATTRIBUTE_CHANGED; + // log orgType changed + if (!org.getOrgType().equals(json.optString(Org.JSON_ORG_TYPE))) { + createAndLogDetails(id, Org.JSON_ORG_TYPE, org.getOrgType(), json.optString(Org.JSON_ORG_TYPE), type); + } + + if (!org.getAddress().equals(json.optString(Org.JSON_ADDRESS))) { + createAndLogDetails(id, Org.JSON_ADDRESS, org.getAddress(), json.optString(Org.JSON_ADDRESS), type); + } + // log description changed + if (!org.getDescription().equals(json.optString(Org.JSON_DESCRIPTION))) { + createAndLogDetails(id, Org.JSON_DESCRIPTION, org.getDescription(), json.optString(Org.JSON_DESCRIPTION), + type); + } + // log web site url changed + if (!org.getUrl().equals(json.optString(Org.JSON_URL))) { + createAndLogDetails(id, Org.JSON_URL, org.getUrl(), json.optString(Org.JSON_URL), type); + } + // log logo changed + if (!org.getLogo().equals(json.get("logo"))) { + createAndLogDetails(id, Org.JSON_LOGO, org.getLogo(), json.getString("logo"), type); + } + // log note changed + if (!org.getNote().equals(json.optString(Org.JSON_NOTE))) { + createAndLogDetails(id, Org.JSON_NOTE, org.getNote(), json.optString(Org.JSON_NOTE), type); + } + } + + /** + * Compare each attributes between original and modified account Log changed as + * JSON For each modification from body request + * + * @param modified Account + * @param original Account before change + */ + public void logChanges(Account modified, Account original) { + String target = modified.getUid(); + final AdminLogType type = AdminLogType.USER_ATTRIBUTE_CHANGED; + + if (modified.getDescription() != null && !modified.getDescription().equals(original.getDescription())) { + // log description changed + createAndLogDetails(target, UserSchema.DESCRIPTION_KEY, original.getDescription(), + modified.getDescription(), type); + } + if (modified.getUid() != null && !modified.getUid().equals(original.getUid())) { + // log uid changed + createAndLogDetails(target, UserSchema.UID_KEY, original.getUid(), modified.getUid(), type); + } + if (modified.getCommonName() != null && !modified.getCommonName().equals(original.getCommonName())) { + // log CN changed + createAndLogDetails(target, UserSchema.COMMON_NAME_KEY, original.getCommonName(), modified.getCommonName(), + type); + } + if (modified.getSurname() != null && !modified.getSurname().equals(original.getSurname())) { + // log SN changed + createAndLogDetails(target, UserSchema.SURNAME_KEY, original.getSurname(), modified.getSurname(), type); + } + if (modified.getEmail() != null && !modified.getEmail().equals(original.getEmail())) { + // log email changed + createAndLogDetails(target, UserSchema.MAIL_KEY, original.getEmail(), modified.getEmail(), type); + } + if (modified.getOrg() != null && !modified.getOrg().equals(original.getOrg())) { + // log org changed + createAndLogDetails(target, UserSchema.ORG_KEY, original.getOrg(), modified.getOrg(), type); + } + if (modified.getPhone() != null && !modified.getPhone().equals(original.getPhone())) { + // log phone changed + createAndLogDetails(target, UserSchema.TELEPHONE_KEY, original.getPhone(), modified.getPhone(), type); + } + if (modified.getPostalAddress() != null && !modified.getPostalAddress().equals(original.getPostalAddress())) { + // log postal adress changed + createAndLogDetails(target, UserSchema.POSTAL_ADDRESS_KEY, original.getPostalAddress(), + modified.getPostalAddress(), type); + } + if (modified.getGivenName() != null && !modified.getGivenName().equals(original.getGivenName())) { + // log GN changed + createAndLogDetails(target, UserSchema.GIVEN_NAME_KEY, original.getGivenName(), modified.getGivenName(), + type); + } + if (modified.getTitle() != null && !modified.getTitle().equals(original.getTitle())) { + // log title changed + createAndLogDetails(target, UserSchema.TITLE_KEY, original.getTitle(), modified.getTitle(), type); + } + if (modified.getPostOfficeBox() != null && !modified.getPostOfficeBox().equals(original.getPostOfficeBox())) { + // log post office changed + createAndLogDetails(target, UserSchema.POST_OFFICE_BOX_KEY, original.getPostOfficeBox(), + modified.getPostOfficeBox(), type); + } + if (modified.getStreet() != null && !modified.getStreet().equals(original.getStreet())) { + // log street changed + createAndLogDetails(target, UserSchema.STREET_KEY, original.getStreet(), modified.getStreet(), type); + } + if (modified.getLocality() != null && !modified.getLocality().equals(original.getLocality())) { + // log L changed + createAndLogDetails(target, UserSchema.LOCALITY_KEY, original.getLocality(), modified.getLocality(), type); + } + if (modified.getFacsimile() != null && !modified.getFacsimile().equals(original.getFacsimile())) { + // log fax changed + createAndLogDetails(target, UserSchema.FACSIMILE_KEY, original.getFacsimile(), modified.getFacsimile(), + type); + } + + if (modified.getPostalCode() != null && !modified.getPostalCode().equals(original.getPostalCode())) { + // log postal code changed + createAndLogDetails(target, UserSchema.POSTAL_CODE_KEY, original.getPostalCode(), modified.getPostalCode(), + type); + } + if (modified.getContext() != null && !modified.getContext().equals(original.getContext())) { + // log context changed + createAndLogDetails(target, UserSchema.CONTEXT_KEY, original.getContext(), modified.getContext(), type); + } + if (modified.getNote() != null && !modified.getNote().equals(original.getNote())) { + // log note changed + createAndLogDetails(target, "note", original.getNote(), modified.getNote(), type); + } + + // special cases when the attribute changed to get null value + String oldValue; + String newValue; + if (modified.getShadowExpire() == null || original.getShadowExpire() == null) { + // log shadow expire changed + oldValue = original.getShadowExpire() != null ? original.getShadowExpire().toString() : ""; + newValue = modified.getShadowExpire() != null ? modified.getShadowExpire().toString() : ""; + if (!newValue.equals(oldValue)) { + createAndLogDetails(target, UserSchema.SHADOW_EXPIRE_KEY, oldValue, newValue, type); + } + } + if (modified.getPrivacyPolicyAgreementDate() == null || original.getPrivacyPolicyAgreementDate() == null) { + // log privacy policy agreement date changed + oldValue = original.getPrivacyPolicyAgreementDate() != null + ? original.getPrivacyPolicyAgreementDate().toString() + : ""; + newValue = modified.getPrivacyPolicyAgreementDate() != null + ? modified.getPrivacyPolicyAgreementDate().toString() + : ""; + if (!newValue.equals(oldValue)) { + createAndLogDetails(target, UserSchema.PRIVACY_POLICY_AGREEMENT_DATE_KEY, oldValue, newValue, type); + } + } + if (modified.getManager() == null || original.getManager() == null) { + // log privacy policy agreement date changed + oldValue = original.getManager() != null ? original.getManager().toString() : ""; + newValue = modified.getManager() != null ? modified.getManager().toString() : ""; + if (!newValue.equals(oldValue)) { + createAndLogDetails(target, UserSchema.MANAGER_KEY, oldValue, newValue, type); + } + } + } +} diff --git a/console/src/main/java/org/georchestra/console/ws/utils/PasswordUtils.java b/console/src/main/java/org/georchestra/console/ws/utils/PasswordUtils.java new file mode 100644 index 0000000000..0cbf9a6000 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/utils/PasswordUtils.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.utils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; +import org.springframework.validation.Errors; + +/** + * + * @author Mauricio Pazos + * + */ +public final class PasswordUtils { + + public static final int SIZE = 8; + + @Autowired + private Validation validation; + + public void setValidation(Validation validation) { + this.validation = validation; + } + + public void validate(final String password, final String confirmPassword, Errors errors) { + + final String pwd1 = password.trim(); + final String pwd2 = confirmPassword.trim(); + + if (!StringUtils.hasLength(pwd1) && validation.isUserFieldRequired("password")) + errors.rejectValue("password", "password.error.required", "required"); + + if (!StringUtils.hasLength(pwd2) && validation.isUserFieldRequired("confirmPassword")) + errors.rejectValue("confirmPassword", "confirmPassword.error.required", "required"); + + if (StringUtils.hasLength(pwd1) && StringUtils.hasLength(pwd2)) { + if (!pwd1.equals(pwd2)) { + errors.rejectValue("confirmPassword", "confirmPassword.error.pwdNotEquals", + "These passwords don't match"); + } else { + if (pwd1.length() < SIZE) + errors.rejectValue("password", "password.error.sizeError", + "The password does have at least 8 characters"); + } + } + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/utils/RecaptchaUtils.java b/console/src/main/java/org/georchestra/console/ws/utils/RecaptchaUtils.java new file mode 100644 index 0000000000..1be3b162b5 --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/utils/RecaptchaUtils.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.utils; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.georchestra.console.ReCaptchaV2; +import org.georchestra.console.bs.ReCaptchaParameters; +import org.springframework.validation.Errors; + +/** + * Utility class to manage recaptcha. Updated to use recaptcha V2 + * + * @author Sylvain Lesage + * + */ +public class RecaptchaUtils { + + private static final Log LOG = LogFactory.getLog(RecaptchaUtils.class.getName()); + + private RecaptchaUtils() { + + } + + /** + * This validate from server side the captchaV2 response from client + * + * @param reCaptchaParameters to get url call and captcha private key + * @param gRecaptchaResponse g-recaptcha-reponse from client side + * @param errors Errors already existing, and used to display + * validation errors + * + * @throws IOException + */ + public static void validate(ReCaptchaParameters reCaptchaParameters, String gRecaptchaResponse, Errors errors) { + + if (gRecaptchaResponse == null || "".equals(gRecaptchaResponse)) { + LOG.info("The user response to recaptcha is empty."); + errors.rejectValue("recaptcha_response_field", "recaptcha_response_field.error.required", "required"); + } else { + ReCaptchaV2 rec = new ReCaptchaV2(); + if (!rec.isValid(reCaptchaParameters.getVerifyUrl(), reCaptchaParameters.getPrivateKey(), + gRecaptchaResponse)) { + errors.rejectValue("recaptcha_response_field", "recaptcha.incorrect", "Validation error"); + } else { + LOG.debug("The user response to recaptcha is valid."); + } + } + } + +} diff --git a/console/src/main/java/org/georchestra/console/ws/utils/Validation.java b/console/src/main/java/org/georchestra/console/ws/utils/Validation.java new file mode 100644 index 0000000000..84eaca2c2c --- /dev/null +++ b/console/src/main/java/org/georchestra/console/ws/utils/Validation.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.console.ws.utils; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.util.StringUtils; +import org.springframework.validation.Errors; + +/** + * Validation class for user and org forms + * + * Possible values: * + * + * There are hardcoded mandatory fields for user and organizations creation: + * + * mandatory user fields: * email * uid * password * confirmPassword + * + * mandatory org fields: * name + * + */ +public class Validation { + + private Set requiredUserFields; + private Set requiredOrgFields; + + /** + * Create a validation class with field list formated as comma separated list. + * List can contains spaces. + * + * @param requiredFields comma separated list of required fields (ex: "surname, + * orgType, orgAddress") + */ + public Validation(String requiredFields) { + + String[] configuredFields = requiredFields.split("\\s*,\\s*"); + this.requiredUserFields = new HashSet(); + this.requiredOrgFields = new HashSet(); + + // Add mandatory fields for user + this.requiredUserFields.add("email"); + this.requiredUserFields.add("uid"); + this.requiredUserFields.add("password"); + this.requiredUserFields.add("confirmPassword"); + + // Add mandatory field for org + this.requiredOrgFields.add("name"); + this.requiredOrgFields.add("shortName"); + this.requiredOrgFields.add("type"); + + // Extract all fields starting by Org and change next letter to lower case + // orgShortName --> shortName + Pattern regexp = Pattern.compile("^org([A-Z].*)$"); + for (String field : configuredFields) { + field = field.trim(); + if (field.length() == 0) + continue; + Matcher m = regexp.matcher(field); + if (m.matches()) { + // This is a org field, so remove 'org' prefix + String match = m.group(1); + match = match.substring(0, 1).toLowerCase() + match.substring(1); + this.requiredOrgFields.add(match); + } else { + // This is a user field + this.requiredUserFields.add(field); + } + } + } + + /** + * Return a set of required fields for user creation or update + * + * @return a Set that contains all required fields for user forms. + */ + public Set getRequiredUserFields() { + return this.requiredUserFields; + } + + /** + * Return a set of required fields for organization creation or update + * + * @return a Set that contains all required fields for organization forms. + */ + public Set getRequiredOrgFields() { + return this.requiredOrgFields; + } + + /** + * Return true if specified field is required for user creation or update + * + * @param field field to check requirement + * @return true id 'field' is required for user forms + */ + public boolean isUserFieldRequired(String field) { + return this.requiredUserFields.contains(field); + } + + /** + * Return true if specified field is required for organization creation or + * update + * + * @param field field to check requirement + * @return true id 'field' is required for organization forms + */ + public boolean isOrgFieldRequired(String field) { + return this.requiredOrgFields.contains(field); + } + + public void validateUserField(String field, String value, Errors errors) { + if (!validateUserField(field, value)) + errors.rejectValue(field, "error.required", "required"); + } + + public boolean validateUserFieldWithSpecificMsg(String field, String value, Errors errors) { + if (!validateUserField(field, value)) { + errors.rejectValue(field, String.format("%s.error.required", field), "required"); + return false; + } + return true; + } + + protected boolean validateUserField(String field, String value) { + return !this.isUserFieldRequired(field) || StringUtils.hasLength(value); + } + + public void validatePrivacyPolicyAgreedField(boolean value, Errors errors) { + if (!value) + errors.rejectValue("privacyPolicyAgreed", "error.required", "required"); + } + + public boolean validateOrgField(String field, JSONObject json) { + try { + return !this.isOrgFieldRequired(field) || (json.has(field) && StringUtils.hasLength(json.getString(field))); + } catch (JSONException e) { + return false; + } + } + + public void validateOrgField(String field, String value, Errors errors) { + if (!validateOrgField(field, value)) { + errors.rejectValue(String.format("org%s", StringUtils.capitalize(field)), "error.required", "required"); + } + } + + public boolean validateOrgField(String field, String value) { + return !this.isOrgFieldRequired(field) || StringUtils.hasLength(value); + } + + public boolean validateUrl(String value) { + if (value == null || value.length() == 0) { + return true; + } + try { + new URL(value); + return true; + } catch (MalformedURLException e) { + return false; + } + } + + public boolean validateUrlFieldWithSpecificMsg(String fullyQualifiedField, String value, Errors errors) { + if (!validateUrl(value)) { + errors.rejectValue(fullyQualifiedField, "error.badUrl", "badUrl"); + return false; + } + return true; + } +} diff --git a/ldapadmin/src/main/java/org/georchestra/lib/file/FileUtils.java b/console/src/main/java/org/georchestra/lib/file/FileUtils.java similarity index 79% rename from ldapadmin/src/main/java/org/georchestra/lib/file/FileUtils.java rename to console/src/main/java/org/georchestra/lib/file/FileUtils.java index f493a72e51..85ffb1bf02 100644 --- a/ldapadmin/src/main/java/org/georchestra/lib/file/FileUtils.java +++ b/console/src/main/java/org/georchestra/lib/file/FileUtils.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + package org.georchestra.lib.file; import java.io.BufferedInputStream; @@ -17,14 +36,15 @@ import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; - /** *

* Common file operations *

*

- * Note : this utility class was copied from extractorapp, the specific functions where removed + * Note : this utility class was copied from extractorapp, the specific + * functions where removed *

+ * * @author jeichar */ public final class FileUtils { @@ -42,9 +62,10 @@ public static void delete(File file) { file.delete(); } - public static void zipDir(ZipOutputStream zip,File baseFile, File file) throws IOException { - if(!file.getPath().startsWith(baseFile.getPath())) { - throw new IOException("When performing a Zip all files must be within the baseFile: "+file+" not contained by "+baseFile); + public static void zipDir(ZipOutputStream zip, File baseFile, File file) throws IOException { + if (!file.getPath().startsWith(baseFile.getPath())) { + throw new IOException("When performing a Zip all files must be within the baseFile: " + file + + " not contained by " + baseFile); } if (file.isDirectory()) { @@ -52,14 +73,14 @@ public static void zipDir(ZipOutputStream zip,File baseFile, File file) throws I zipDir(zip, baseFile, f); } } else { - String relativeName = file.getPath().substring(baseFile.getParent().length()+1); + String relativeName = file.getPath().substring(baseFile.getParent().length() + 1); ZipEntry next = new ZipEntry(relativeName); zip.putNextEntry(next); FileInputStream in = new FileInputStream(file); try { - in.getChannel().transferTo(0, file.length(), Channels.newChannel(zip)); + in.getChannel().transferTo(0, file.length(), Channels.newChannel(zip)); } finally { - in.close(); + in.close(); } } } @@ -128,8 +149,7 @@ public static String getZipEntryAsString(File archive, String name) throws IOExc } } - public static void moveFile(File from, File to) throws FileNotFoundException, - IOException { + public static void moveFile(File from, File to) throws FileNotFoundException, IOException { if (!from.renameTo(to)) { FileInputStream in = new FileInputStream(from); FileOutputStream out = new FileOutputStream(to); @@ -150,10 +170,10 @@ public static String asString(InputStream inputStream) throws IOException { StringBuilder buffer = new StringBuilder(); try { String line = in.readLine(); - while(line!=null){ + while (line != null) { buffer.append(line); buffer.append("\n"); - line=in.readLine(); + line = in.readLine(); } } finally { in.close(); diff --git a/console/src/main/java/org/georchestra/lib/springutils/GeorchestraUserDetailsService.java b/console/src/main/java/org/georchestra/lib/springutils/GeorchestraUserDetailsService.java new file mode 100644 index 0000000000..e8769d6ab5 --- /dev/null +++ b/console/src/main/java/org/georchestra/lib/springutils/GeorchestraUserDetailsService.java @@ -0,0 +1,45 @@ +package org.georchestra.lib.springutils; + +import static org.georchestra.commons.security.SecurityHeaders.SEC_ROLES; + +import java.util.Collection; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.georchestra.commons.security.SecurityHeaders; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +public class GeorchestraUserDetailsService + implements AuthenticationUserDetailsService { + + public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) throws AuthenticationException { + Assert.notNull(token.getDetails()); + return createUserDetails(token, null); + } + + protected UserDetails createUserDetails(Authentication token, Collection authorities) { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()) + .getRequest(); + String secRoles = SecurityHeaders.decode(request.getHeader(SEC_ROLES)); + List auth = StringUtils.isEmpty(secRoles) ? AuthorityUtils.NO_AUTHORITIES + : semicolonSeparatedStringToAuthorityList(secRoles); + + return new User(token.getName(), "N/A", true, true, true, true, auth); + } + + public static List semicolonSeparatedStringToAuthorityList(String authorityString) { + return AuthorityUtils.createAuthorityList(StringUtils.tokenizeToStringArray(authorityString, ";")); + } +} diff --git a/console/src/main/java/org/georchestra/lib/sqlcommand/AbstractDataCommand.java b/console/src/main/java/org/georchestra/lib/sqlcommand/AbstractDataCommand.java new file mode 100644 index 0000000000..621ec90d78 --- /dev/null +++ b/console/src/main/java/org/georchestra/lib/sqlcommand/AbstractDataCommand.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.lib.sqlcommand; + +import javax.sql.DataSource; + +public abstract class AbstractDataCommand implements DataCommand { + + protected DataSource dataSource; + + @Override + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } +} diff --git a/console/src/main/java/org/georchestra/lib/sqlcommand/AbstractQueryCommand.java b/console/src/main/java/org/georchestra/lib/sqlcommand/AbstractQueryCommand.java new file mode 100644 index 0000000000..544de69a69 --- /dev/null +++ b/console/src/main/java/org/georchestra/lib/sqlcommand/AbstractQueryCommand.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.lib.sqlcommand; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Maintains the abstract behavior required to execute a SQL query. The subclass + * must implement the methods: + * + *
+ * {@link AbstractQueryCommand#prepareStatement()}
+ * {@link AbstractQueryCommand#getRow(ResultSet)}
+ * 
+ * + * @author Mauricio Pazos + */ +public abstract class AbstractQueryCommand extends AbstractDataCommand { + + private LinkedList> resultList; + + /** + * This template method executes the sql statement specified in the + * prepareStatement method. + */ + @Override + public void execute() throws DataCommandException { + + assert (this.dataSource != null) : "database connection pool is null, use setDataSource"; + + // executes the sql statement and populates the list with the data present in + // the result set + try (Connection c = dataSource.getConnection(); // + PreparedStatement pStmt = prepareStatement(c); // + ResultSet rs = pStmt.executeQuery()) { + + this.resultList = new LinkedList>(); + + while (rs.next()) { + this.resultList.add(getRow(rs)); + } + + } catch (SQLException e) { + throw new DataCommandException(e.getMessage(), e); + } + } + + /** + * The subclass must to define the sql statement to exectue + * + * @return {@link PreparedStatement}} + * @throws SQLException + */ + protected abstract PreparedStatement prepareStatement(Connection connection) throws SQLException; + + /** + * Assigns the values of fields present in the {@link ResultSet} to the Map. + * + * @param rs + * @return a Map + * @throws SQLException + */ + protected abstract Map getRow(ResultSet rs) throws SQLException; + + public List> getResult() { + return this.resultList; + } + +} diff --git a/console/src/main/java/org/georchestra/lib/sqlcommand/AbstractUpdateCommand.java b/console/src/main/java/org/georchestra/lib/sqlcommand/AbstractUpdateCommand.java new file mode 100644 index 0000000000..4f9067d7a9 --- /dev/null +++ b/console/src/main/java/org/georchestra/lib/sqlcommand/AbstractUpdateCommand.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.lib.sqlcommand; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * Executes Insert, Update and Delete SQL command. + * + *

+ * The subclass must provide the sql command to execute. To do that the + * {@link AbstractUpdateCommand#prepareStatement()} method + *

+ * + * + * @author Mauricio Pazos + * + */ +public abstract class AbstractUpdateCommand extends AbstractDataCommand { + + /** + * Execute the sql insert to add the new row (uid, token, timestamp) + * + * @see org.georchestra.ogcservstatistics.dataservices.DataCommand#execute() + */ + @Override + public void execute() throws DataCommandException { + assert (this.dataSource != null) : "database connection pool is null, use setDataSource"; + + // executes the sql statement and checks that the update operation will be + // inserted one row in the table + try (Connection connection = dataSource.getConnection()) { + connection.setAutoCommit(false); + try (PreparedStatement pStmt = prepareStatement(connection)) { + int updatedRows = pStmt.executeUpdate(); + connection.commit(); + if (updatedRows < 1) { + throw new DataCommandException("Database update produced no changes: " + pStmt.toString()); + } + } catch (SQLException statementError) { + connection.rollback(); + throw new DataCommandException(statementError); + } finally { + connection.setAutoCommit(true); + } + } catch (SQLException e) { + throw new DataCommandException(e); + } + } + + /** + * The subclass should provide a method to prepare Insert, Update or Delete + * + * @return {@link PreparedStatement} + * @throws SQLException + */ + protected abstract PreparedStatement prepareStatement(Connection connection) throws SQLException; + +} diff --git a/console/src/main/java/org/georchestra/lib/sqlcommand/DataCommand.java b/console/src/main/java/org/georchestra/lib/sqlcommand/DataCommand.java new file mode 100644 index 0000000000..a2517d8eb2 --- /dev/null +++ b/console/src/main/java/org/georchestra/lib/sqlcommand/DataCommand.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.lib.sqlcommand; + +import javax.sql.DataSource; + +/** + * @author Mauricio Pazos + * + */ +public interface DataCommand { + + /** + * @param dataSource connection provider + */ + public void setDataSource(DataSource dataSource); + + /** + * Execute the sql command specified + * + * @throws DataCommandException + */ + public void execute() throws DataCommandException; + +} diff --git a/console/src/main/java/org/georchestra/lib/sqlcommand/DataCommandException.java b/console/src/main/java/org/georchestra/lib/sqlcommand/DataCommandException.java new file mode 100644 index 0000000000..141e118d83 --- /dev/null +++ b/console/src/main/java/org/georchestra/lib/sqlcommand/DataCommandException.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 by the geOrchestra PSC + * + * This file is part of geOrchestra. + * + * geOrchestra is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * geOrchestra is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * geOrchestra. If not, see . + */ + +package org.georchestra.lib.sqlcommand; + +import java.sql.SQLException; + +public class DataCommandException extends Exception { + + /** + * for serialization + */ + private static final long serialVersionUID = -5196425322579527757L; + + public DataCommandException(String message) { + super(message); + } + + public DataCommandException(SQLException e) { + super(e); + } + + public DataCommandException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/console/src/main/resources/META-INF/spring/applicationContext.xml b/console/src/main/resources/META-INF/spring/applicationContext.xml new file mode 100644 index 0000000000..aa6853b29d --- /dev/null +++ b/console/src/main/resources/META-INF/spring/applicationContext.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + diff --git a/ldapadmin/src/main/resources/log4j.properties b/console/src/main/resources/log4j.properties similarity index 100% rename from ldapadmin/src/main/resources/log4j.properties rename to console/src/main/resources/log4j.properties diff --git a/console/src/main/webapp/WEB-INF/classes/log4j.properties b/console/src/main/webapp/WEB-INF/classes/log4j.properties new file mode 100644 index 0000000000..c9cddfec49 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/classes/log4j.properties @@ -0,0 +1,28 @@ +#------------------------------------------------------------------------------ +# +# The following properties set the logging levels and log appender. The +# log4j.rootLogger variable defines the default log level and one or more +# appenders. For the console, use 'S'. For the daily rolling file, use 'R'. +# For an HTML formatted log, use 'H'. +# +# To override the default (rootLogger) log level, define a property of the +# form (see below for available values): +# +# log4j.logger. = +# +# Possible Log Levels: +# FATAL, ERROR, WARN, INFO, DEBUG +# +#------------------------------------------------------------------------------ +log4j.rootLogger=WARN, R + +log4j.logger.org.georchestra.console=WARN, R +log4j.logger.org.georchestra.console.ws.utils=INFO, R + +log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender +log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy +log4j.appender.R.RollingPolicy.FileNamePattern = /tmp/console.%d.log.gz +log4j.appender.R.RollingPolicy.ActiveFileName = /tmp/console.log +log4j.appender.R.Append = true +log4j.appender.R.layout = org.apache.log4j.PatternLayout +log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n diff --git a/ldapadmin/src/main/webapp/WEB-INF/classes/persistence.xml b/console/src/main/webapp/WEB-INF/classes/persistence.xml similarity index 100% rename from ldapadmin/src/main/webapp/WEB-INF/classes/persistence.xml rename to console/src/main/webapp/WEB-INF/classes/persistence.xml diff --git a/console/src/main/webapp/WEB-INF/i18n/application.properties b/console/src/main/webapp/WEB-INF/i18n/application.properties new file mode 100644 index 0000000000..9b4c6c1d70 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/i18n/application.properties @@ -0,0 +1,145 @@ +#Updated at Thu Jun 13 21:39:28 CEST 2013 +#Thu Jun 13 21:39:28 CEST 2013 + +### General + +submit.label=Submit +form.error=The form contains errors + +### Forms + +changePasswordForm.title=Change password. +changePasswordForm.subtitle=Enter your new password. +changePasswordForm.fieldset.password=Password +changePasswordForm.success=Password updated successfully +createAccountForm.title=New account. +createAccountForm.subtitle=Create your account. +createAccountForm.description=An email will be sent to the above address to confirm the account creation. +createAccountForm.fieldset.userDetails=User details +createAccountForm.fieldset.credentials=Credentials +createAccountForm.fieldset.privacyPolicyAgreement=Terms and conditions +editUserDetailsForm.title=User details. +editUserDetailsForm.subtitle=Update your account. +editUserDetailsForm.description=In this page you can manage your account informations and modify your password. +editUserDetailsForm.fieldset.userDetails=User details +editUserDetailsForm.fieldset.credentials=Credentials +editUserDetailsForm.changePassword.link=Change Password +editUserDetailsForm.success=User details updated successfully +editUserDetailsForm.organisation=Organization +editUserDetailsForm.url=Website +editUserDetailsForm.areaOfCompetence=Area of competence +editUserDetailsForm.members=Members +editUserDetailsForm.editOrg=Edit Organization +editOrgDetailsForm.title=Organization details +editOrgDetailsForm.name=Name +editOrgDetailsForm.shortName=Short Name +editOrgDetailsForm.description=Description +editOrgDetailsForm.success=Success +editOrgDetailsForm.fieldset.orgDetails=Details +editUserDetailsForm.gdpr=GDPR +editUserDetailsForm.download=Download my data +editUserDetailsForm.delete=Delete my account +editUserDetailsForm.downloadMsg=In accordance with the European General Data Protection Regulation, you are able to download your data on this platform at any time. +editUserDetailsForm.deleteMsg=You may also permanently delete your account. WARNING: this cannot be undone ! +passwordRecoveryForm.title=Ask for a new password. +passwordRecoveryForm.subtitle=You've lost your password or your account was just created. +passwordRecoveryForm.description=You will receive an email at the following address with a reset link to let you set a new password. +passwordRecoveryForm.fieldset.email=Get new password +passwordRecoveryForm.externalPasswordWarningMessage=You are going to send a recovery message. This user password is managed by an external system. This user password will be handled by geOrchestra and not your external system. +newPasswordForm.title=Reset password. +newPasswordForm.subtitle=Enter a new password. +newPasswordForm.description=You requested a new password. Please enter your new password. +newPasswordForm.fieldset.password=Password +newPasswordForm.success=Password updated successfully + +### Other JSP pages + +dataAccessFailure.title=Data access failure +emailWasSent.body=An e-mail was sent with a link for updating password. Please, check your e-mail box. +emailWasSent.link=Home +emailWasSent.title=E-mail sent +forbidden.body=Sorry, but you don't have access to this area. +forbidden.link=Home +forbidden.title=Forbidden +newUserWelcome.body=Your request has been submitted and should be processed in the next hours. Watch your e-mail box. +newUserWelcome.link=Log in +newUserWelcome.title=Request submitted +passwordUpdated.body=The password has been successfully updated. You will now be able to log in. +passwordUpdated.link=Log in +passwordUpdated.title=Password updated +resourceNotFound.title=Resource not found +uncaughtException.title=Internal Error +userManagedBySASL.pageTitle=Cannot modify password +userManagedBySASL.title=User password cannot be modified +userManagedBySASL.body=User is managed by an external system. Contact your administrator. + +### Form fields properties + +confirmPassword.label=Confirm password +confirmPassword.placeholder=Confirm password +confirmPassword.error.required=Required +confirmPassword.error.pwdNotEquals=These passwords don\'t match +confirmPassword.error.pwdNotEquals.tag=Mismatch +description.label=Description +description.placeholder=Description +email.label=E-mail +email.placeholder=E-mail +email.error.required=Required +email.error.invalidFormat=Invalid format +email.error.exist=This e-mail is already in use +email.error.notFound=No user is registered with this email. +error.required=Required +error.badUrl=malformed url +facsimile.label=Fax +facsimile.placeholder=Fax +firstName.label=First Name +firstName.placeholder=First Name +firstName.error.required=Required +org.label=Organization +org.placeholder=Organization +org.empty=empty +org.cannot_find_org_in_list=My organization is not present +org.required=Required +org.creation.label=Organization Name +org.creation.shortLabel=Short Name +org.creation.address=Address +org.creation.orgType=Organization type +org.creation.orgDescription=Description +org.creation.orgUrl=Url +org.creation.orgLogo=Logo +org.shortNameFormat=Short name must only contain alphanumeric characters +password.label=Password +password.placeholder=Password +password.error.required=Required +password.error.sizeError=The password must contain at least 8 characters +password.label.empty=empty +password.label.good=good +password.label.strong=strong +password.label.veryweak=very weak +password.label.weak=weak +postalAddress.label=Postal address +postalAddress.placeholder=Postal address +phone.label=Phone +phone.placeholder=Phone +phone.error.nonNumeric=The phone number should only contain digits +privacyPolicyAgreed.label=Conditions +privacyPolicyAgreed.checkboxLabel=I read and agree to the conditions +privacyPolicyAgreed.error.notChecked=Please accept the conditions, in order to create the account +recaptcha_response_field.error.required=Required +recaptcha.incorrect=Incorrect, please try again +surname.label=Surname +surname.placeholder=Surname +surname.error.required=Required +title.label=Job title +title.placeholder=Job title +uid.label=User ID +uid.placeholder=User ID +uid.error.required=user identifier is required +uid.error.exist=This identifier has already been selected. Please choose another one. +uid.error.invalid=Invalid uid. It must begin with a letter followed by one or more letters, digits, \"-\" (hyphens) or \".\" (dots) +url.label=Url +logo.label=Logo +shortName.label=Short name +name.label=Name +address.label=Address +orgType.label=Organization Type diff --git a/console/src/main/webapp/WEB-INF/i18n/application_de.properties b/console/src/main/webapp/WEB-INF/i18n/application_de.properties new file mode 100644 index 0000000000..aaf5bd4bc3 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/i18n/application_de.properties @@ -0,0 +1,145 @@ +#Updated at Thu Jun 13 21:39:28 CEST 2013 +#Thu Jun 13 21:39:28 CEST 2013 + +### General + +submit.label=Senden +form.error=Das Formular enthält Fehler + +### Forms + +changePasswordForm.title=Passwort ändern. +changePasswordForm.subtitle=Geben Sie Ihr neues Passwort ein. +changePasswordForm.fieldset.password=Passwort +changePasswordForm.success=Passwort erfolgreich aktualisiert +createAccountForm.title=Neues Konto. +createAccountForm.subtitle=Erstellen Sie Ihr Konto. +createAccountForm.description=Eine E-Mail wird an die Adresse geschickt, um die Account-Erstellung zu bestätigen. +createAccountForm.fieldset.userDetails=Benutzerdetails +createAccountForm.fieldset.credentials=Referenzen +createAccountForm.fieldset.privacyPolicyAgreement=Geschäftsbedingungen +editUserDetailsForm.title=Benutzerdetails. +editUserDetailsForm.subtitle=Aktualisieren Sie Ihr Konto. +editUserDetailsForm.description=Auf dieser Seite können Sie Ihre Kontoinformationen verwalten und Ihr Passwort ändern. +editUserDetailsForm.fieldset.userDetails=Benutzerdetails +editUserDetailsForm.fieldset.credentials=Referenzen +editUserDetailsForm.changePassword.link=Passwort ändern +editUserDetailsForm.success=Benutzerdetails erfolgreich aktualisiert +editUserDetailsForm.organisation=Organisation +editUserDetailsForm.url=Webseite +editUserDetailsForm.areaOfCompetence=Kompetenzbereich +editUserDetailsForm.members=Mitglieder +editUserDetailsForm.editOrg=Organisation bearbeiten +editOrgDetailsForm.title=Organisationsdetails +editOrgDetailsForm.name=Name +editOrgDetailsForm.shortName=Kurzname +editOrgDetailsForm.description=Beschreibung +editOrgDetailsForm.success=Erfolg +editOrgDetailsForm.fieldset.orgDetails=Details +editUserDetailsForm.gdpr=GDPR +editUserDetailsForm.download=Meine Daten herunterladen +editUserDetailsForm.delete=Mein Konto löschen +editUserDetailsForm.downloadMsg=In Übereinstimmung mit der Europäischen Allgemeinen Datenschutzverordnung können Sie Ihre Daten jederzeit auf dieser Plattform herunterladen. +editUserDetailsForm.deleteMsg=Sie können Ihr Konto auch dauerhaft löschen. WARNUNG: Dies kann nicht rückgängig gemacht werden! +passwordRecoveryForm.title=Ein neues Password anfordern. +passwordRecoveryForm.subtitle=Sie haben Ihr Passwort vergessen oder Ihr Konto wurde gerade erst erstellt. +passwordRecoveryForm.description=Wir senden Ihnen eine E-Mail an die folgende Adresse mit einem Link um das Password zurückzusetzen. +passwordRecoveryForm.fieldset.email=Neues Passwort anfordern +passwordRecoveryForm.externalPasswordWarningMessage=You are going to send a recovery message. This user password is managed by an external system. This user password will be handled by geOrchestra and not your external system. +newPasswordForm.title=Passwort zurücksetzen. +newPasswordForm.subtitle=Ein neues Passwort eingeben. +newPasswordForm.description=Bitte geben Sie Ihr neues Passwort ein. +newPasswordForm.fieldset.password=Passwort +newPasswordForm.success=Passwort erfolgreich aktualisiert + +### Other JSP pages + +dataAccessFailure.title=Datenzugriffsfehler +emailWasSent.body=Eine E-Mail mit einem Link zum Zurücksetzen des Passworts wurde versendet. Bitte überprüfen Sie Ihren Posteingang. +emailWasSent.link=Home +emailWasSent.title=E-Mail gesendet +forbidden.body=Entschuldigung, aber Sie haben keinen Zugang zu diesem Bereich. +forbidden.link=Home +forbidden.title=Geschützter Bereich +newUserWelcome.body=Ihre Anfrage wurde eingereicht und sollte in den nächsten Stunden bearbeitet werden. Kontrollieren Sie bitte Ihren Posteingang. +newUserWelcome.link=Anmelden +newUserWelcome.title=Anfrage abgesendet +passwordUpdated.body=Das Password wurde erfolgreich geändert. Sie können sich nun einloggen. +passwordUpdated.link=Anmelden +passwordUpdated.title=Passwort aktualisiert +resourceNotFound.title=Ressource wurde nicht gefunden +uncaughtException.title=Interner Fehler +userManagedBySASL.pageTitle=Cannot modify password +userManagedBySASL.title=User password cannot be modified +userManagedBySASL.body=User is managed by an external system. Contact your administrator. + +### Form fields properties + +confirmPassword.label=Passwort bestätigen +confirmPassword.placeholder=Passwort bestätigen +confirmPassword.error.required=Notwendig +confirmPassword.error.pwdNotEquals=Passwörter stimmen nicht überein +confirmPassword.error.pwdNotEquals.tag=Nicht das gleiche Passwort +description.label=Beschreibung +description.placeholder=Beschreibung +email.label=E-Mail +email.placeholder=E-Mail +email.error.required=Notwendig +email.error.invalidFormat=Ungültiges Format +email.error.exist=Es existiert bereits ein Account mit dieser E-Mail Adresse. +email.error.notFound=Kein Benutzer ist mit dieser E-Mail registriert. +error.required=Notwendig +error.badUrl=malformed url +facsimile.label=Fax +facsimile.placeholder=Fax +firstName.label=Vorname +firstName.placeholder=Vorname +firstName.error.required=Notwendig +org.label=Organisation +org.placeholder=Organisation +org.empty=leer +org.cannot_find_org_in_list=Meine Organisation ist nicht vorhanden +org.required=Notwendig +org.creation.label=Organisations-Name +org.creation.shortLabel=Kurzbezeichnung +org.creation.address=Adresse +org.creation.orgType=Organisationstyp +org.creation.orgDescription=Beschreibung +org.creation.orgUrl=Url +org.creation.orgLogo=Logo +org.shortNameFormat=Die Kurzbezeichnung darf nur alphanumerische Zeichen enthalten +password.label=Passwort +password.placeholder=Passwort +password.error.required=Notwendig +password.error.sizeError=Das Passwort muss mindestens 8 Zeichen enthalten +password.label.empty=leer +password.label.good=gut +password.label.strong=stark +password.label.veryweak=sehr schwach +password.label.weak=schwach +postalAddress.label=Postanschrift +postalAddress.placeholder=Postanschrift +phone.label=Telefon +phone.placeholder=Telefon +phone.error.nonNumeric=Die Telefonnummer sollte nur Ziffern enthalten +privacyPolicyAgreed.label=Conditions +privacyPolicyAgreed.checkboxLabel=I read and agree to the conditions +privacyPolicyAgreed.error.notChecked=Please accept the conditions, in order to create the account +recaptcha_response_field.error.required=Notwendig +recaptcha.incorrect=Falsch, bitte versuchen es noch einmal +surname.label=Name +surname.placeholder=Name +surname.error.required=Notwendig +title.label=Titel +title.placeholder=Titel +uid.label=Benutzer-ID +uid.placeholder=Benutzer-ID +uid.error.required=Benutzer-ID ist notwendig +uid.error.exist=Dieses Benutzer-ID ist bereits ausgewählt. Bitte wählen Sie einen anderen. +uid.error.invalid=Ungültige UID. Die UID muss mit einem Buchstaben beginnen, gefolgt von einem oder mehreren Buchstaben, Ziffern, \"-\" (Bindestrich) oder \".\" (Punkte). +url.label=Url +logo.label=Logo +shortName.label=Kurzname +name.label=Name +address.label=Adresse +orgType.label=Organisationstyp diff --git a/console/src/main/webapp/WEB-INF/i18n/application_es.properties b/console/src/main/webapp/WEB-INF/i18n/application_es.properties new file mode 100644 index 0000000000..670c414f12 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/i18n/application_es.properties @@ -0,0 +1,147 @@ +#Updated at Thu Jun 13 21:39:28 CEST 2013 +#Thu Jun 13 21:39:28 CEST 2013 + +### General + +submit.label=Enviar +form.error=El formulario tiene errores + +### Forms + +changePasswordForm.title=Cambio de contraseña. +changePasswordForm.subtitle=Ingrese su nueva contraseña. +changePasswordForm.fieldset.password=Contraseña +changePasswordForm.success=Contraseña actualizada con éxito +createAccountForm.title=Nueva cuenta. +createAccountForm.subtitle=Solicite la creación de una cuenta. +createAccountForm.description=Un correo de confirmación será mandado a la dirección indicada. +createAccountForm.fieldset.userDetails=Detalles del usuario +createAccountForm.fieldset.credentials=Credenciales +createAccountForm.fieldset.privacyPolicyAgreement=Condiciones de uso +editUserDetailsForm.title=Detalles de la cuenta. +editUserDetailsForm.subtitle=Actualice sus informaciones. +editUserDetailsForm.description=En esta página, puede manejar las informaciones de su cuenta y modificar su contraseña. +editUserDetailsForm.fieldset.userDetails=Detalles del usuario +editUserDetailsForm.fieldset.credentials=Credenciales +editUserDetailsForm.changePassword.link=Cambiar contraseña +editUserDetailsForm.success=Detalles actualizados con éxito +editUserDetailsForm.organisation=Organización +editUserDetailsForm.url=Sitio web +editUserDetailsForm.areaOfCompetence=Área de competencia +editUserDetailsForm.members=Miembros +editUserDetailsForm.editOrg=Editar organización +editOrgDetailsForm.title=Detalles de la organización +editOrgDetailsForm.name=Nombre +editOrgDetailsForm.shortName=Nombre corto +editOrgDetailsForm.description=Descripción +editOrgDetailsForm.success=Éxito +editOrgDetailsForm.fieldset.orgDetails=Detalles +editUserDetailsForm.gdpr=GDPR +editUserDetailsForm.download=Descargando mis datos +editUserDetailsForm.delete=Borrar mi cuenta +editUserDetailsForm.downloadMsg=De acuerdo con el Reglamento general europeo de protección de datos, puede descargar sus datos en esta plataforma en cualquier momento. +editUserDetailsForm.deleteMsg=También puede eliminar permanentemente su cuenta. ADVERTENCIA: esto no se puede deshacer! +passwordRecoveryForm.title=Solicite una contraseña. +passwordRecoveryForm.subtitle=Ha perdido su contraseña o su cuenta ha sido creada. +passwordRecoveryForm.description=Recibirá un correo electrónico a la dirección siguiente con un vínculo para establecer una nueva contraseña de su elección. +passwordRecoveryForm.fieldset.email=Obtener una nueva contraseña +passwordRecoveryForm.externalPasswordWarningMessage= +newPasswordForm.title=Recuperación de contraseña. +newPasswordForm.subtitle=Ingrese una nueva contraseña. +newPasswordForm.description=Usted le pedirá que cree una nueva contraseña. Por favor ingrese su nueva contraseña. +newPasswordForm.fieldset.password=Contraseña +newPasswordForm.success=Contraseña actualizada con éxito + +### Other JSP pages + +dataAccessFailure.title=Error de acceso a los datos +emailWasSent.body=Un correo electrónico ha sido enviado con un vínculo para actualizar la nueva contraseña. Por favor, revise sus correos. +emailWasSent.link=Inicio +emailWasSent.title=Correo electrónico enviado +forbidden.body=No tiene acceso a esta sección. +forbidden.link=Ir al inicio +forbidden.title=Prohibido +newUserWelcome.body=Su solicitud ha sido enviada y será procesada en las próximas horas. Revise sus correos. +newUserWelcome.link=Conectarse +newUserWelcome.title=Solicitud enviada +passwordUpdated.body=La contraseña fue actualizada con éxito. Puede conectarse ahora. +passwordUpdated.link=Conectarse +passwordUpdated.title=Contraseña actualizada +resourceNotFound.title=Recurso no encontrado +uncaughtException.title=Error interna +userManagedBySASL.pageTitle=Cannot modify password +userManagedBySASL.title=User password cannot be modified +userManagedBySASL.body=User is managed by an external system. Contact your administrator. + +### Form fields properties + +confirmPassword.label=Confirmar contraseña +confirmPassword.placeholder=Confirmar contraseña +confirmPassword.error.required=Requerida +confirmPassword.error.pwdNotEquals=Las contraseñas son diferentes +confirmPassword.error.pwdNotEquals.tag=Diferentes +description.label=Descripción +description.placeholder=Descripción +email.label=dirección de correo electrónico +email.placeholder=dirección de correo electrónico +email.error.required=Requerido +email.error.invalidFormat=Formato inválido +email.error.exist=Esta dirección de correo electrónico ya está en uso. +email.error.notFound=Ningún usuario esta registrado con esta dirección de correo electrónico. +error.required=Requerido +error.badUrl=malformed url +facsimile.label=Fax +facsimile.placeholder=Fax +firstName.label=Nombre +firstName.placeholder=Nombre +firstName.error.required=Requerido +org.label=Organización +org.placeholder=Organización +org.empty=vacío +org.cannot_find_org_in_list=Mi organización no esta presente +org.required=Requerida +org.creation.label=Nombre de la Organización +org.creation.shortLabel=Nombre corto +org.creation.address=Dirección +org.creation.orgType=Tipo de Organización +org.creation.orgDescription=Descripción +org.creation.orgUrl=Url +org.creation.orgLogo=Logo +org.shortNameFormat=El nombre corto solo debe contener caracteres alfanuméricos +password.label=Contraseña +password.placeholder=Contraseña +password.error.required=Requerida +password.error.sizeError=La contraseña debe tener al menos 8 caracteres +password.label.empty=vacío +password.label.good=bueno +password.label.strong=fuerte +password.label.veryweak=muy débil +password.label.weak=débil +postalAddress.label=Dirección +postalAddress.placeholder=Dirección +phone.label=Teléfono +phone.placeholder=Teléfono +phone.error.nonNumeric=El número de teléfono solo puede contener dígitos +privacyPolicyAgreed.label=Condiciones +privacyPolicyAgreed.checkboxLabel=Leí y acepto las condiciones +privacyPolicyAgreed.error.notChecked=Por favor, aceptar las condiciones para crear la cuenta +postalAddress.label=Dirección postal +postalAddress.placeholder=Dirección postal +recaptcha_response_field.error.required=Requerido +recaptcha.incorrect=Incorrecto, por favor pruebe de nuevo +surname.label=Apellido +surname.placeholder=Apellido +surname.error.required=Requerido +title.label=Puesto +title.placeholder=Puesto +uid.label=ID Usuario +uid.placeholder=Identificador del usuario +uid.error.required=Requerido +uid.error.exist=Este identificador ya fue seleccionado. Por favor elija otro. +uid.error.invalid=uid invalido. Debe comenzar por una letra y estar seguido de uno o más letras, dígitos, guión (\"-\") o puntos (\".\") +url.label=Url +logo.label=Logo +shortName.label=Nombre corto +name.label=Nombre +address.label=Dirección +orgType.label=Tipo de organización diff --git a/console/src/main/webapp/WEB-INF/i18n/application_fr.properties b/console/src/main/webapp/WEB-INF/i18n/application_fr.properties new file mode 100644 index 0000000000..850c7e1691 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/i18n/application_fr.properties @@ -0,0 +1,145 @@ +#Updated at Thu Jun 13 21:39:28 CEST 2013 +#Thu Jun 13 21:39:28 CEST 2013 + +### General + +submit.label=Envoyer +form.error=Le formulaire contient des erreurs + +### Forms + +changePasswordForm.title=Changement de mot de passe. +changePasswordForm.subtitle=Entrez votre nouveau mot de passe. +changePasswordForm.fieldset.password=Mot de passe +changePasswordForm.success=Mot de passe actualisé +createAccountForm.title=Nouveau compte. +createAccountForm.subtitle=Sollicitez la création d'un compte. +createAccountForm.description=Un courriel de confirmation sera envoyé à l'adresse indiquée. +createAccountForm.fieldset.userDetails=Détails du compte +createAccountForm.fieldset.credentials=Identification +createAccountForm.fieldset.privacyPolicyAgreement=Conditions d'utilisation +editUserDetailsForm.title=Détails du compte. +editUserDetailsForm.subtitle=Actualisez vos informations. +editUserDetailsForm.description=Sur cette page vous pouvez gérer les informations de votre compte, et modifier votre mot de passe. +editUserDetailsForm.fieldset.userDetails=Détails du compte +editUserDetailsForm.fieldset.credentials=Identification +editUserDetailsForm.changePassword.link=Changer le mot de passe +editUserDetailsForm.success=Compte actualisé +editUserDetailsForm.organisation=Organisme +editUserDetailsForm.url=Site web +editUserDetailsForm.areaOfCompetence=Zone de compétence +editUserDetailsForm.members=Membres +editUserDetailsForm.editOrg=Modifier l'organisme +editOrgDetailsForm.title=Détails de l'organisme +editOrgDetailsForm.name=Nom +editOrgDetailsForm.shortName=Nom court +editOrgDetailsForm.description=Description +editOrgDetailsForm.success=Succès +editOrgDetailsForm.fieldset.orgDetails=Détails +editUserDetailsForm.gdpr=Protection des données +editUserDetailsForm.download=Télécharger mes données +editUserDetailsForm.delete=Supprimer mon compte +editUserDetailsForm.downloadMsg=Conformément à la règlementation sur la protection des données, vous pouvez à tout moment télécharger vos données. +editUserDetailsForm.deleteMsg=Vous pouvez également demander la suppression de votre compte. ATTENTION : cette action est irréversible. +passwordRecoveryForm.title=Demande de mot de passe. +passwordRecoveryForm.subtitle=Vous avez perdu votre mot de passe ou votre compte a été créé. +passwordRecoveryForm.description=Vous recevrez par courriel un lien vous permettant de définir votre mot de passe. +passwordRecoveryForm.fieldset.email=Obtenir un nouveau mot de passe +passwordRecoveryForm.externalPasswordWarningMessage=Vous êtes sur le point d'envoyer un message de remise à 0 du mot de passe utilisateur. Le mot de passe de cet utilisateur est géré par un système externe. Le mot de passe de cet utilisateur sera dès lors géré par geOrchestra. +newPasswordForm.title=Récupération du mot de passe. +newPasswordForm.subtitle=Entrez un nouveau mot de passe. +newPasswordForm.description=Vous avez demandé la création d'un nouveau mot de passe. Veuillez saisir votre nouveau mot de passe. +newPasswordForm.fieldset.password=Mot de passe +newPasswordForm.success=Mot de passe actualisé + +### Other JSP pages + +dataAccessFailure.title=Erreur d'accès aux données +emailWasSent.body=Un courriel a été envoyé à l'instant pour vous permettre de changer votre mot de passe. +emailWasSent.link=Portail +emailWasSent.title=Courriel envoyé +forbidden.body=Désolé, vous n'avez pas accès à cette page. +forbidden.link=Portail +forbidden.title=Accès interdit +newUserWelcome.body=Votre demande d'inscription a été prise en compte et sera traitée dans les meilleurs délais. +newUserWelcome.link=Se connecter +newUserWelcome.title=Demande d'inscription prise en compte +passwordUpdated.body=Le mot de passe a été mis à jour. Vous pouvez maintenant vous connecter. +passwordUpdated.link=Se connecter +passwordUpdated.title=Mot de passe mis à jour +resourceNotFound.title=Ressource introuvable +uncaughtException.title=Erreur interne +userManagedBySASL.pageTitle=Mot de passe non modifiable +userManagedBySASL.title=Le mot de passe utilisateur ne peut être modifié +userManagedBySASL.body=Cet utilisateur est géré par un système distant. Veuillez contacter votre administrateur. + +### Form fields properties + +confirmPassword.label=Confirmation du mot de passe +confirmPassword.placeholder=Confirmation du mot de passe +confirmPassword.error.required=Requis +confirmPassword.error.pwdNotEquals=Les mots de passe sont différents +confirmPassword.error.pwdNotEquals.tag=Différents +description.label=Description +description.placeholder=Description +email.label=Courriel +email.placeholder=Courriel +email.error.required=Requis +email.error.invalidFormat=Format invalide +email.error.exist=Ce courriel est déjà utilisé +email.error.notFound=Aucun utilisateur n'est enregistré avec ce courriel. +error.required=Requis +error.badUrl=Format d'url invalide +facsimile.label=Fax +facsimile.placeholder=Fax +firstName.label=Prénom +firstName.placeholder=Prénom +firstName.error.required=Requis +org.label=Organisme +org.placeholder=Organisme +org.empty=vide +org.cannot_find_org_in_list=Mon organisme n'apparaît pas dans la liste +org.required=Requis +org.creation.label=Votre organisme +org.creation.shortLabel=Libellé court +org.creation.address=Adresse +org.creation.orgType=Type d\'organisme +org.creation.orgDescription=Description +org.creation.orgUrl=Url +org.creation.orgLogo=Logo +org.shortNameFormat=Le nom court ne doit comporter que des caractères alphanumériques +password.label=Mot de passe +password.placeholder=Mot de passe +password.error.required=Requis +password.error.sizeError=Le mot de passe doit contenir au moins 8 caractères +password.label.empty=vide +password.label.good=bon +password.label.strong=fort +password.label.veryweak=très faible +password.label.weak=faible +postalAddress.label=Adresse postale +postalAddress.placeholder=Adresse postale +phone.label=Téléphone +phone.placeholder=Téléphone +phone.error.nonNumeric=Le numéro de téléphone doit contenir uniquement des chiffres +privacyPolicyAgreed.label=Conditions +privacyPolicyAgreed.checkboxLabel=J''ai lu et j''accepte les conditions +privacyPolicyAgreed.error.notChecked=Veuillez accepter les conditions afin de créer le compte +recaptcha_response_field.error.required=Cochez la case +recaptcha.incorrect=Erreur. Réessayez +surname.label=Nom +surname.placeholder=Nom +surname.error.required=Requis +title.label=Fonction +title.placeholder=Fonction +uid.label=Login +uid.placeholder=Login +uid.error.required=Requis +uid.error.exist=Ce login est déjà utilisé. Choisissez-en un autre. +uid.error.invalid=Login invalide. Il doit commencer par une lettre et être suivi par un ou plusieurs chiffres, lettres, tirets ou points. +url.label=Url +logo.label=Logo +shortName.label=Nom court +name.label=Nom +address.label=Adresse +orgType.label=Type d'organisme diff --git a/console/src/main/webapp/WEB-INF/i18n/application_nl.properties b/console/src/main/webapp/WEB-INF/i18n/application_nl.properties new file mode 100644 index 0000000000..e93085416a --- /dev/null +++ b/console/src/main/webapp/WEB-INF/i18n/application_nl.properties @@ -0,0 +1,117 @@ +#Updated at Thu Jun 13 21:39:28 CEST 2013 +#Thu Jun 13 21:39:28 CEST 2013 + +### General + +submit.label=Sturen +form.error=Het formulier bevat fouten + +### Forms + +changePasswordForm.title=Wachtwoord wijzigen. +changePasswordForm.subtitle=Voer uw nieuwe wachtwoord in. +changePasswordForm.fieldset.password=Wachtwoord +changePasswordForm.success=Wachtwoord bijgewerkt +createAccountForm.title=Nieuw account. +createAccountForm.subtitle=Verzoek om het aanmaken van een account. +createAccountForm.description=Een bevestigingsmail zal naar het aangegeven adres worden verzonden. +createAccountForm.fieldset.userDetails=Accountgegevens +createAccountForm.fieldset.credentials=Identificatie +editUserDetailsForm.title=Accountgegevens. +editUserDetailsForm.subtitle=Bijwerk uw gegevens. +editUserDetailsForm.description=Op deze pagina kunt u uw accountinformatie beheren en uw wachtwoord wijzigen. +editUserDetailsForm.fieldset.userDetails=Accountgegevens +editUserDetailsForm.fieldset.credentials=Identificatie +editUserDetailsForm.changePassword.link=Wachtwoord wijzigen +editUserDetailsForm.success=Account bijgewerkt +editUserDetailsForm.organisation=Organisatie +editUserDetailsForm.areaOfCompetence=Gebied +editUserDetailsForm.members=Leden +passwordRecoveryForm.title=Wachtwoord aanvragen. +passwordRecoveryForm.subtitle=U bent uw wachtwoord kwijt of uw account is aangemaakt. +passwordRecoveryForm.description=U ontvangt per email een link waarmee u uw wachtwoord kunt definiëren. +passwordRecoveryForm.fieldset.email=Vraag een nieuw wachtwoord aan +passwordRecoveryForm.externalPasswordWarningMessage= +newPasswordForm.title=Wachtwoordherstel. +newPasswordForm.subtitle=Voer een nieuw wachtwoord in. +newPasswordForm.description=U hebt gevraagd om een nieuw wachtwoord aan te maken. Voer je nieuwe wachtwoord in. +newPasswordForm.fieldset.password=Wachtwoord +newPasswordForm.success=Wachtwoord bijgewerkt + +### Other JSP pages + +dataAccessFailure.title=Fout bij gegevenstoegang +emailWasSent.body=Er is direct een email verzonden waarmee u uw wachtwoord kunt wijzigen. +emailWasSent.link=Portal +emailWasSent.title=Email verzonden +forbidden.body=Sorry, u hebt geen toegang tot deze pagina. +forbidden.link=Portal +forbidden.title=Toegang verboden +newUserWelcome.body=Uw registratieverzoek is in aanmerking genomen en zal zo snel mogelijk worden verwerkt. +newUserWelcome.link=Inloggen +newUserWelcome.title=Registratie-aanvraag in aanmerking genomen +passwordUpdated.body=Het wachtwoord is bijgewerkt. U kunt nu inloggen. +passwordUpdated.link=Inloggen +passwordUpdated.title=Wachtwoord bijgewerkt +resourceNotFound.title=Hulpbron niet gevonden +uncaughtException.title=Interne fout +userManagedBySASL.pageTitle=Cannot modify password +userManagedBySASL.title=User password cannot be modified +userManagedBySASL.body=User is managed by an external system. Contact your administrator. + +### Form fields properties + +confirmPassword.label=Bevestig wachtwoord +confirmPassword.placeholder=Bevestig wachtwoord +confirmPassword.error.required=Verplicht +confirmPassword.error.pwdNotEquals=Wachtwoorden zijn verschillend +confirmPassword.error.pwdNotEquals.tag=Verschillend +description.label=Beschrijving +description.placeholder=Beschrijving +email.label=Email +email.placeholder=Email +email.error.required=Verplicht +email.error.invalidFormat=Ongeldig formaat +email.error.exist=Deze e-mail is al in gebruik +email.error.notFound=Er is geen gebruiker geregistreerd bij deze e-mail. +error.required=Verplicht +facsimile.label=Fax +facsimile.placeholder=Fax +firstName.label=Voornaam +firstName.placeholder=Voornaam +firstName.error.required=Verplicht +org.label=Organisatie +org.placeholder=Organisatie +org.empty=leeg +org.cannot_find_org_in_list=Mijn organisatie verschijnt niet in de lijst +org.required=Verplicht +org.creation.label=Uw organisatie +org.creation.shortLabel=Kort label +org.creation.address=Adres +org.creation.orgType=Type organisatie +password.label=Wachtwoord +password.placeholder=Wachtwoord +password.error.required=Verplicht +password.error.sizeError=Wachtwoord moet minimaal 8 tekens bevatten +password.label.empty=leeg +password.label.good=goed +password.label.strong=sterk +password.label.veryweak=erg zwak +password.label.weak=zwak +postalAddress.label=Postadres +postalAddress.placeholder=Postadres +phone.label=Telefoon +phone.placeholder=Telefoon +phone.error.nonNumeric=Telefoonnummer mag alleen nummers bevatten +recaptcha_response_field.error.required=Vink het vakje aan +recaptcha.incorrect=Fout. Probeer het opnieuw +surname.label=Naam +surname.placeholder=Naam +surname.error.required=Verplicht +title.label=Functie +title.placeholder=Functie +uid.label=Login +uid.placeholder=Login +uid.error.required=Verplicht +uid.error.exist=Deze login is al in gebruik. Kies een andere. +uid.error.invalid=Ongeldige login. Het moet beginnen met een letter en worden gevolgd door een of meer cijfers, letters, streepjes of punten. diff --git a/console/src/main/webapp/WEB-INF/jetty-web.xml b/console/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 0000000000..797db4810a --- /dev/null +++ b/console/src/main/webapp/WEB-INF/jetty-web.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/console/src/main/webapp/WEB-INF/spring/applicationContext-security.xml b/console/src/main/webapp/WEB-INF/spring/applicationContext-security.xml new file mode 100644 index 0000000000..41fad34aa2 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/spring/applicationContext-security.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/console/src/main/webapp/WEB-INF/spring/webmvc-config.xml b/console/src/main/webapp/WEB-INF/spring/webmvc-config.xml new file mode 100644 index 0000000000..d82d3912f9 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/spring/webmvc-config.xml @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Comma separated list of one or more user identifiers (uid) of protected user + + ${protectedUsersList:geoserver_privileged_user} + + + + + + Comma separated list of one or more protected Roles + + ${protectedRolesList:ADMINISTRATOR,EXTRACTORAPP,GN_.*,ORGADMIN,REFERENT,USER,SUPERUSER} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.hibernate.dialect.PostgreSQL94Dialect + false + false + update + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + dataAccessFailure + resourceNotFound + resourceNotFound + resourceNotFound + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ldapadmin/src/main/webapp/WEB-INF/tags/input.tag b/console/src/main/webapp/WEB-INF/tags/input.tag similarity index 85% rename from ldapadmin/src/main/webapp/WEB-INF/tags/input.tag rename to console/src/main/webapp/WEB-INF/tags/input.tag index fe5e8697a5..1a059ddb76 100644 --- a/ldapadmin/src/main/webapp/WEB-INF/tags/input.tag +++ b/console/src/main/webapp/WEB-INF/tags/input.tag @@ -7,6 +7,7 @@ <%@attribute name="cssClass" required="false" type="java.lang.String"%> <%@attribute name="label" required="false" type="java.lang.String"%> <%@attribute name="required" required="false" type="java.lang.Boolean"%> +<%@attribute name="readonly" required="false" type="java.lang.Boolean"%> <%@attribute name="onchange" required="false" type="java.lang.String"%> <%@attribute name="onkeyup" required="false" type="java.lang.String"%> <%@attribute name="appendIcon" required="false" type="java.lang.String"%> @@ -20,7 +21,7 @@
- +
diff --git a/console/src/main/webapp/WEB-INF/tags/list.tag b/console/src/main/webapp/WEB-INF/tags/list.tag new file mode 100644 index 0000000000..887458456f --- /dev/null +++ b/console/src/main/webapp/WEB-INF/tags/list.tag @@ -0,0 +1,37 @@ +<%@tag description="Extended select tag to allow for sophisticated errors" pageEncoding="UTF-8"%> +<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> +<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@attribute name="path" required="true" type="java.lang.String"%> +<%@attribute name="items" required="true" type="java.util.Map"%> +<%@attribute name="cssClass" required="false" type="java.lang.String"%> +<%@attribute name="label" required="false" type="java.lang.String"%> +<%@attribute name="required" required="false" type="java.lang.Boolean"%> +<%@attribute name="onchange" required="false" type="java.lang.String"%> +<%@attribute name="onkeyup" required="false" type="java.lang.String"%> +<%@attribute name="appendIcon" required="false" type="java.lang.String"%> + + + + +
+ +
+ +
+ + + + + + + +
+
+ + ${status.errorMessage} + +
+
+
diff --git a/ldapadmin/src/main/webapp/WEB-INF/tags/password.tag b/console/src/main/webapp/WEB-INF/tags/password.tag similarity index 100% rename from ldapadmin/src/main/webapp/WEB-INF/tags/password.tag rename to console/src/main/webapp/WEB-INF/tags/password.tag diff --git a/console/src/main/webapp/WEB-INF/tags/privacyPolicyAgreement.tag b/console/src/main/webapp/WEB-INF/tags/privacyPolicyAgreement.tag new file mode 100644 index 0000000000..2d24ea3708 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/tags/privacyPolicyAgreement.tag @@ -0,0 +1,21 @@ +<%@tag description="Link to the privacy policy and checkbox to capture the user agreement" pageEncoding="UTF-8"%> +<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> +<%@attribute name="path" required="true" type="java.lang.String"%> +<%@attribute name="label" required="false" type="java.lang.String"%> +<%@attribute name="checkboxLabel" required="false" type="java.lang.String"%> +<%@attribute name="required" required="false" type="java.lang.Boolean"%> + + +
+ +
+ + + + ${status.errorMessage} + +
+
+
diff --git a/console/src/main/webapp/WEB-INF/tags/recaptcha.tag b/console/src/main/webapp/WEB-INF/tags/recaptcha.tag new file mode 100644 index 0000000000..c1f1216820 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/tags/recaptcha.tag @@ -0,0 +1,35 @@ +<%@tag description="Extended recaptcha tag to allow for sophisticated errors" pageEncoding="UTF-8"%> +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@attribute name="path" required="true" type="java.lang.String"%> + + +
+ +
+ + ${status.errorMessage} + +
+ + + + diff --git a/ldapadmin/src/main/webapp/WEB-INF/tags/textarea.tag b/console/src/main/webapp/WEB-INF/tags/textarea.tag similarity index 100% rename from ldapadmin/src/main/webapp/WEB-INF/tags/textarea.tag rename to console/src/main/webapp/WEB-INF/tags/textarea.tag diff --git a/console/src/main/webapp/WEB-INF/templates/README.md b/console/src/main/webapp/WEB-INF/templates/README.md new file mode 100644 index 0000000000..e8ec5c8e0d --- /dev/null +++ b/console/src/main/webapp/WEB-INF/templates/README.md @@ -0,0 +1,22 @@ +# Console email templates + +This folder hosts email templates which are sent to users and platform administrators at various times. + +Either account creation is moderated or it is not. +This is set with the `moderatedSignup` variable from the [console.properties](/console/console.properties) file, which defaults to `true`. + +When account creation is moderated: + * [newaccount-requires-moderation-template.txt](newaccount-requires-moderation-template.txt) is sent to members of the SUPERUSER role and also to users holding a delegation (if any) for the organisation that was declared by the new user. + * [account-creation-in-progress-template.txt](account-creation-in-progress-template.txt) is sent to the requesting user (this is an ACK mail, the account is pending moderation). + * [newaccount-was-created-template.txt](newaccount-was-created-template.txt) is sent to the requesting user upon account validation, his account is now active. + +When account creation is not moderated: + * [newaccount-notification-template.txt](newaccount-notification-template.txt) is sent to members of the SUPERUSER role and also to users holding a delegation (if any) for the organisation that was declared by the new user. + * [newaccount-was-created-template.txt](newaccount-was-created-template.txt) is sent to the requesting user (this is a welcoming email). + +In both cases, it's the responsibility of the platform admin to inform the user that his account was granted roles. + +[account-uid-renamed.txt](account-uid-renamed.txt) is sent to the user whose login have been modified by a platform admin. + +[changepassword-email-template.txt](changepassword-email-template.txt) is sent when the user requests a new password with the "I lost my password" link from the CAS login page. +It is also sent to the user when the administrator triggers a password regeneration from the admin console. diff --git a/console/src/main/webapp/WEB-INF/templates/account-creation-in-progress-template.txt b/console/src/main/webapp/WEB-INF/templates/account-creation-in-progress-template.txt new file mode 100644 index 0000000000..b90aeb450b --- /dev/null +++ b/console/src/main/webapp/WEB-INF/templates/account-creation-in-progress-template.txt @@ -0,0 +1,8 @@ +Dear {name}, + +Your request for a new account will be processed very soon. + +Your login is: {uid} + +--- +Sent by {instanceName} ({publicUrl}/) diff --git a/console/src/main/webapp/WEB-INF/templates/account-uid-renamed.txt b/console/src/main/webapp/WEB-INF/templates/account-uid-renamed.txt new file mode 100644 index 0000000000..e165cb3852 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/templates/account-uid-renamed.txt @@ -0,0 +1,9 @@ +Dear {name}, + +This message is intended to let you know that your identifier on the +{instanceName} platform has been modified. + +Your new login is now: {uid} + +--- +Sent by {instanceName} ({publicUrl}/) diff --git a/console/src/main/webapp/WEB-INF/templates/changepassword-email-template.txt b/console/src/main/webapp/WEB-INF/templates/changepassword-email-template.txt new file mode 100644 index 0000000000..2d5ac3cd2b --- /dev/null +++ b/console/src/main/webapp/WEB-INF/templates/changepassword-email-template.txt @@ -0,0 +1,12 @@ +Dear {name}, + +You (or someone else) asked to reset your password on {publicUrl}/. +If you did not request any password update, just ignore this e-mail, you're safe. + +To set a new password for your user ({uid}), go to {url}. +You will then be able to connect to the platform. + +Caution: this e-mail is personal, don't forward it. + +--- +Sent by {instanceName} ({publicUrl}/) diff --git a/console/src/main/webapp/WEB-INF/templates/newaccount-notification-template.txt b/console/src/main/webapp/WEB-INF/templates/newaccount-notification-template.txt new file mode 100644 index 0000000000..e4e6eb5009 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/templates/newaccount-notification-template.txt @@ -0,0 +1,11 @@ +Dear admin, + +A new user signed up on {publicUrl}/ ! + +User name: {name} +User email: {email} +User ID: {uid} +User Organization: {org} + +--- +Sent by {instanceName} ({publicUrl}/) diff --git a/console/src/main/webapp/WEB-INF/templates/newaccount-requires-moderation-template.txt b/console/src/main/webapp/WEB-INF/templates/newaccount-requires-moderation-template.txt new file mode 100644 index 0000000000..fbbc5c97ca --- /dev/null +++ b/console/src/main/webapp/WEB-INF/templates/newaccount-requires-moderation-template.txt @@ -0,0 +1,12 @@ +Dear admin, + +A new account has been created on {publicUrl}/ and is waiting for validation. + +User name: {name} +User ID: {uid} +User Organization: {org} + +Visit {publicUrl}/console/manager/browse/pending/users?login to review the pending users. + +--- +Sent by {instanceName} ({publicUrl}/) diff --git a/console/src/main/webapp/WEB-INF/templates/newaccount-was-created-template.txt b/console/src/main/webapp/WEB-INF/templates/newaccount-was-created-template.txt new file mode 100644 index 0000000000..30881fe858 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/templates/newaccount-was-created-template.txt @@ -0,0 +1,10 @@ +Dear {name}, + +Your account on {publicUrl}/ has been successfully created ! +Visit {publicUrl}/console/ to login with your identifier "{uid}" and password. + +Have fun with geOrchestra, + +Your platform administrator +--- +Sent by {instanceName} ({publicUrl}/) diff --git a/console/src/main/webapp/WEB-INF/urlrewrite.xml b/console/src/main/webapp/WEB-INF/urlrewrite.xml new file mode 100644 index 0000000000..3ed79f87b6 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/urlrewrite.xml @@ -0,0 +1,11 @@ + + + + + ^/.*/manager/public/.*$ + ^/.*/manager/fonts/.*$ + ^/.*/manager/$ + ^/manager/(.*)$ + /manager/#!/$1 + + diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/changePasswordForm.jsp b/console/src/main/webapp/WEB-INF/views/changePasswordForm.jsp similarity index 83% rename from ldapadmin/src/main/webapp/WEB-INF/views/changePasswordForm.jsp rename to console/src/main/webapp/WEB-INF/views/changePasswordForm.jsp index 408efbe6fd..98047b8594 100644 --- a/ldapadmin/src/main/webapp/WEB-INF/views/changePasswordForm.jsp +++ b/console/src/main/webapp/WEB-INF/views/changePasswordForm.jsp @@ -1,3 +1,24 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> @@ -13,7 +34,7 @@ - + <s:message code="changePasswordForm.title"/> diff --git a/console/src/main/webapp/WEB-INF/views/createAccountForm.jsp b/console/src/main/webapp/WEB-INF/views/createAccountForm.jsp new file mode 100644 index 0000000000..4b6e193d54 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/views/createAccountForm.jsp @@ -0,0 +1,326 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> + +<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %> + + + + + + + + + + + <s:message code="createAccountForm.title"/> + + + + + + + + <%@ include file="header.jsp" %> + +
+ +

+ + + +
+ + ${message} +
+
+ + + +
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + + + + + + +
+ +
+ + + + + + + + + + +
+ + +
+ + + + + + + + + +
+
+ + +
+ +
+
+ +
+
+
+ +
+
+
+
+
+ + + + <%@ include file="validation.jsp" %> + + + diff --git a/console/src/main/webapp/WEB-INF/views/dataAccessFailure.jsp b/console/src/main/webapp/WEB-INF/views/dataAccessFailure.jsp new file mode 100644 index 0000000000..75c3f8470a --- /dev/null +++ b/console/src/main/webapp/WEB-INF/views/dataAccessFailure.jsp @@ -0,0 +1,63 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> + + + + + + + + + + <s:message code="dataAccessFailure.title"/> + + + + + <%@ include file="header.jsp" %> + +
+ +<% +Exception ex = (Exception) request.getAttribute("exception"); +%> +
+<%= ex.getMessage() %>
+		
+
+<%
+ex.printStackTrace(new java.io.PrintWriter(out));
+%>
+		
+
+ + + + diff --git a/console/src/main/webapp/WEB-INF/views/editOrgDetailsForm.jsp b/console/src/main/webapp/WEB-INF/views/editOrgDetailsForm.jsp new file mode 100644 index 0000000000..6375e9780a --- /dev/null +++ b/console/src/main/webapp/WEB-INF/views/editOrgDetailsForm.jsp @@ -0,0 +1,114 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> +<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> +<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %> + + + + + + + + <s:message code="editOrgDetailsForm.title"/> + + + + +<%@ include file="header.jsp" %> + +
+ +
+
+ + + +
+ + +
+
+ + + +
+ + +
+
+
+ +
+ + + + + + + + + + + + + + +
+ + +
+
+ +
+
+
+ +
+
+
+
+
+
+ + logo + +
+
+
+ + + diff --git a/console/src/main/webapp/WEB-INF/views/editUserDetailsForm.jsp b/console/src/main/webapp/WEB-INF/views/editUserDetailsForm.jsp new file mode 100644 index 0000000000..7805ce2f01 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/views/editUserDetailsForm.jsp @@ -0,0 +1,299 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> +<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> +<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %> + + + + + + + + + + <s:message code="editUserDetailsForm.title"/> + + + + +<%@ include file="header.jsp" %> + + + + +
+ +

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

+ ${editUserDetailsFormBean.email} +

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

+ + + ${editUserDetailsFormBean.org} + + + + + + + +

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

+ ${editUserDetailsFormBean.uid} +

+
+
+
+ +
+

+ + + +

+
+
+
+ +
+
+
+ +
+
+
+
+ + + + + + + + + + + +
+
+ + «{{org.name}}» + " + class="small pull-right" aria-label="" + > + + + + +
    +
  • +

    {{org.description}}

    +
  • +
  • + + + +
  • +
  • + + + +
  • +
  • + logo +
  • +
+ +

+ +
+ +

{{ users.length }}

+
    +
  • + {{::user.sn}} {{::user.givenName}} +
  • +
+ + +
+
+ +
+ +
+
+
+

+ +

+

+ +

+
+
+
+
+
+ + +<%@ include file="validation.jsp" %> + + + diff --git a/console/src/main/webapp/WEB-INF/views/emailWasSent.jsp b/console/src/main/webapp/WEB-INF/views/emailWasSent.jsp new file mode 100644 index 0000000000..440b3a09c9 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/views/emailWasSent.jsp @@ -0,0 +1,49 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> + + + + + + + + + + <s:message code="emailWasSent.title" /> + + + + <%@ include file="header.jsp" %> + +
+
+

+

+
+
+ + + + diff --git a/console/src/main/webapp/WEB-INF/views/forbidden.jsp b/console/src/main/webapp/WEB-INF/views/forbidden.jsp new file mode 100644 index 0000000000..c3f07d7e3b --- /dev/null +++ b/console/src/main/webapp/WEB-INF/views/forbidden.jsp @@ -0,0 +1,50 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> + + + + + + + + + + + <s:message code="forbidden.title" /> + + + + <%@ include file="header.jsp" %> + +
+
+

+

+
+
+ + + + diff --git a/console/src/main/webapp/WEB-INF/views/header.jsp b/console/src/main/webapp/WEB-INF/views/header.jsp new file mode 100644 index 0000000000..6cdc9a23ed --- /dev/null +++ b/console/src/main/webapp/WEB-INF/views/header.jsp @@ -0,0 +1,9 @@ +<%@ page language="java" pageEncoding="UTF-8" %> + + + +
+ +
+
+
diff --git a/console/src/main/webapp/WEB-INF/views/managerUi.jsp b/console/src/main/webapp/WEB-INF/views/managerUi.jsp new file mode 100644 index 0000000000..778ace20a5 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/views/managerUi.jsp @@ -0,0 +1 @@ +<%@ include file="../../manager/public/index.html" %> diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/newPasswordForm.jsp b/console/src/main/webapp/WEB-INF/views/newPasswordForm.jsp similarity index 83% rename from ldapadmin/src/main/webapp/WEB-INF/views/newPasswordForm.jsp rename to console/src/main/webapp/WEB-INF/views/newPasswordForm.jsp index 9b1468d397..4ba302134d 100644 --- a/ldapadmin/src/main/webapp/WEB-INF/views/newPasswordForm.jsp +++ b/console/src/main/webapp/WEB-INF/views/newPasswordForm.jsp @@ -1,3 +1,24 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> @@ -13,7 +34,7 @@ - + <s:message code="newPasswordForm.title"/> diff --git a/console/src/main/webapp/WEB-INF/views/passwordRecoveryForm.jsp b/console/src/main/webapp/WEB-INF/views/passwordRecoveryForm.jsp new file mode 100644 index 0000000000..a5af5bff4a --- /dev/null +++ b/console/src/main/webapp/WEB-INF/views/passwordRecoveryForm.jsp @@ -0,0 +1,120 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> +<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> +<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %> + + + + + + + + + + <s:message code="passwordRecoveryForm.title"/> + + + + + + + <%@ include file="header.jsp" %> + +
+ +

+ + +
+ + ${message} +
+
+ + + +
+ + +
+
+
+ +
+ + + + +
+ + +
+ +
+
+ +
+
+
+ +
+
+
+ +
+ + + <%@ include file="validation.jsp" %> + + + diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/passwordUpdated.jsp b/console/src/main/webapp/WEB-INF/views/passwordUpdated.jsp similarity index 85% rename from ldapadmin/src/main/webapp/WEB-INF/views/passwordUpdated.jsp rename to console/src/main/webapp/WEB-INF/views/passwordUpdated.jsp index f012b6e563..333c23216b 100644 --- a/ldapadmin/src/main/webapp/WEB-INF/views/passwordUpdated.jsp +++ b/console/src/main/webapp/WEB-INF/views/passwordUpdated.jsp @@ -9,7 +9,7 @@ - + <s:message code="passwordUpdated.title" /> @@ -20,7 +20,7 @@

-

+

diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/resourceNotFound.jsp b/console/src/main/webapp/WEB-INF/views/resourceNotFound.jsp similarity index 96% rename from ldapadmin/src/main/webapp/WEB-INF/views/resourceNotFound.jsp rename to console/src/main/webapp/WEB-INF/views/resourceNotFound.jsp index 522948f3d1..934d3d6e17 100644 --- a/ldapadmin/src/main/webapp/WEB-INF/views/resourceNotFound.jsp +++ b/console/src/main/webapp/WEB-INF/views/resourceNotFound.jsp @@ -12,7 +12,7 @@ - + <s:message code="resourceNotFound.title"/> diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/uncaughtException.jsp b/console/src/main/webapp/WEB-INF/views/uncaughtException.jsp similarity index 96% rename from ldapadmin/src/main/webapp/WEB-INF/views/uncaughtException.jsp rename to console/src/main/webapp/WEB-INF/views/uncaughtException.jsp index b3b3048ee9..8ac97906e8 100644 --- a/ldapadmin/src/main/webapp/WEB-INF/views/uncaughtException.jsp +++ b/console/src/main/webapp/WEB-INF/views/uncaughtException.jsp @@ -12,7 +12,7 @@ - + <s:message code="uncaughtException.title"/> diff --git a/console/src/main/webapp/WEB-INF/views/userManagedBySASL.jsp b/console/src/main/webapp/WEB-INF/views/userManagedBySASL.jsp new file mode 100644 index 0000000000..26153f5f60 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/views/userManagedBySASL.jsp @@ -0,0 +1,49 @@ +<%-- + + Copyright (C) 2009 by the geOrchestra PSC + + This file is part of geOrchestra. + + geOrchestra is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + geOrchestra is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + geOrchestra. If not, see . + +--%> + +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> + + + +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + + + + <s:message code="userManagedBySASL.pageTitle"/> + + + +<%@ include file="header.jsp" %> + + +
+
+

+

+
+
+ + + + diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/validation.jsp b/console/src/main/webapp/WEB-INF/views/validation.jsp similarity index 83% rename from ldapadmin/src/main/webapp/WEB-INF/views/validation.jsp rename to console/src/main/webapp/WEB-INF/views/validation.jsp index 9c91e04b8e..1e7e52989e 100644 --- a/ldapadmin/src/main/webapp/WEB-INF/views/validation.jsp +++ b/console/src/main/webapp/WEB-INF/views/validation.jsp @@ -18,7 +18,7 @@ function addError(id, errCode) { function testField(field) { removeError(field); if (!isNotEmpty($("#"+field).val()) && isFieldRequired(field)) { - addError(field, ''); + addError(field, ""); return false; } return true; @@ -27,7 +27,7 @@ function testFirstname() { var firstname = document.form.firstName.value; removeError("firstName"); if (!isNotEmpty(firstname) && isFieldRequired("firstName")) { - addError("firstName", ''); + addError("firstName", ""); return false; } return true; @@ -36,7 +36,7 @@ function testSurname() { var surname = document.form.surname.value; removeError("surname"); if (!isNotEmpty(surname) && isFieldRequired("surname")) { - addError("surname", ''); + addError("surname", ""); return false; } return true; @@ -45,10 +45,10 @@ function testEmail() { var email = document.form.email.value; removeError("email"); if (!isNotEmpty(email) && isFieldRequired("email")) { - addError("email", ''); + addError("email", ""); return false; } else if (!emailCheck(email)) { - addError("email", ''); + addError("email", ""); return false; } return true; @@ -57,7 +57,7 @@ function testUid() { var uid = document.form.uid.value; removeError("uid"); if (!isNotEmpty(uid) && isFieldRequired("uid")) { - addError("uid", ''); + addError("uid", ""); return false; } else if (!isUidValid(uid)) { addError("uid", ''); @@ -69,7 +69,7 @@ function testPassword() { var password = document.form.password.value; removeError("password"); if (!isPasswordValid(password)) { - addError("password", ''); + addError("password", ""); return false; } return true; @@ -79,20 +79,47 @@ function testConfirmPassword() { var confirmPassword = document.form.confirmPassword.value; removeError("confirmPassword"); if (password!=confirmPassword) { - addError("confirmPassword", ''); + addError("confirmPassword", ""); return false; } return true; } +function testPrivacyPolicyAgreed(){ + removeError("privacyPolicyAgreed"); + if(!$('#privacyPolicyAgreed').is(':checked')){ + addError("privacyPolicyAgreed", ""); + return false; + } + return true; +} + +/** + * First verification, be sure g-recaptcha-response textarea is not empty + */ function testRecaptcha() { - var recaptcha_response_field = document.form.recaptcha_response_field.value; - removeError("recaptcha_response_field"); + var recaptcha_response_field = document.getElementById("g-recaptcha-response").value; + removeError("g-recaptcha"); if (!isNotEmpty(recaptcha_response_field)) { - addError("recaptcha_response_field", ''); + addError("g-recaptcha", ""); return false; } + /** Server side won't accept attribute with - in it, so reuse the old one */ + document.form.recaptcha_response_field.value=recaptcha_response_field; return true; } +function testOrg(){ + removeError("orgName"); + removeError("orgShortName"); + removeError("orgAddress"); + removeError("orgType"); + removeError("org"); + if($('#createOrg').is(':checked')){ + return testField("orgName") & testField("orgShortName") & testField("orgAddress") & testField("orgType"); + } else { + return testField("org"); + } +} + function setFormError() { $("form#form > #message").remove(); $("form#form").prepend('
'); @@ -163,24 +190,24 @@ function feedbackPassStrength(elId, password){ } }); if (!password){ - message = ''; + message = ""; msgLabelClass = "default"; } else { var score = scorePassword(password); if (score > 80){ - message= ''; + message= ""; msgLabelClass = "success"; msgOffsetClass = "8"; } else if (score > 60) { - message = ''; + message = ""; msgLabelClass = "info"; msgOffsetClass = "6"; } else if (score >= 30) { - message = ''; + message = ""; msgLabelClass = "warning"; msgOffsetClass = "4"; } else { - message = ''; + message = ""; msgLabelClass = "danger"; msgOffsetClass = "2"; } @@ -228,6 +255,9 @@ function isUidValid(uid) { * @returns {boolean} */ function isNotEmpty(str) { + if(str == "-") { + return false; + } if (str.trim().length){ return true; } else { @@ -266,7 +296,7 @@ function emailCheck (emailStr) { /* The following is the list of known TLDs that an e-mail address must end with. */ - var knownDomsPat=/^(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum)$/; + var knownDomsPat=/^(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum|bzh)$/; /* The following pattern is used to check if the entered e-mail address fits the user@domain format. It also is used to separate the username diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/welcomeNewUser.jsp b/console/src/main/webapp/WEB-INF/views/welcomeNewUser.jsp similarity index 85% rename from ldapadmin/src/main/webapp/WEB-INF/views/welcomeNewUser.jsp rename to console/src/main/webapp/WEB-INF/views/welcomeNewUser.jsp index e35ba1b7fb..5345531337 100644 --- a/ldapadmin/src/main/webapp/WEB-INF/views/welcomeNewUser.jsp +++ b/console/src/main/webapp/WEB-INF/views/welcomeNewUser.jsp @@ -9,7 +9,7 @@ - + <s:message code="newUserWelcome.title" /> @@ -20,7 +20,7 @@

-

+

diff --git a/console/src/main/webapp/WEB-INF/web.xml b/console/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..ee5a86ce47 --- /dev/null +++ b/console/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,104 @@ + + + console + console application + + + contextConfigLocation + + classpath*:META-INF/spring/applicationContext*.xml + WEB-INF/spring/applicationContext*.xml + + + + org.eclipse.jetty.servlet.Default.dirAllowed + false + + + org.springframework.web.context.request.RequestContextListener + + + + org.springframework.web.context.ContextLoaderListener + + + + console + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + WEB-INF/spring/webmvc-config.xml + + 1 + + + + console + / + /manager/public/ + + + default + /account/css/* + /account/fonts/* + /account/js/* + /manager/public/* + /manager/fonts/* + + + 10 + + + java.lang.Exception + /WEB-INF/views/uncaughtException.jsp + + + 404 + /WEB-INF/views/resourceNotFound.jsp + + + 403 + /WEB-INF/views/forbidden.jsp + + + encodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + UTF-8 + + + forceEncoding + true + + + + UrlRewriteFilter + org.tuckey.web.filters.urlrewrite.UrlRewriteFilter + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + encodingFilter + /* + + + UrlRewriteFilter + /* + + + springSecurityFilterChain + /* + + + + + + diff --git a/ldapadmin/src/main/webapp/account/css/bootstrap-theme.css b/console/src/main/webapp/account/css/bootstrap-theme.css similarity index 100% rename from ldapadmin/src/main/webapp/account/css/bootstrap-theme.css rename to console/src/main/webapp/account/css/bootstrap-theme.css diff --git a/ldapadmin/src/main/webapp/account/css/bootstrap-theme.min.css b/console/src/main/webapp/account/css/bootstrap-theme.min.css similarity index 100% rename from ldapadmin/src/main/webapp/account/css/bootstrap-theme.min.css rename to console/src/main/webapp/account/css/bootstrap-theme.min.css diff --git a/ldapadmin/src/main/webapp/account/css/bootstrap.css b/console/src/main/webapp/account/css/bootstrap.css similarity index 100% rename from ldapadmin/src/main/webapp/account/css/bootstrap.css rename to console/src/main/webapp/account/css/bootstrap.css diff --git a/ldapadmin/src/main/webapp/account/css/bootstrap.min.css b/console/src/main/webapp/account/css/bootstrap.min.css similarity index 100% rename from ldapadmin/src/main/webapp/account/css/bootstrap.min.css rename to console/src/main/webapp/account/css/bootstrap.min.css diff --git a/console/src/main/webapp/account/css/console.css b/console/src/main/webapp/account/css/console.css new file mode 100644 index 0000000000..28f138375f --- /dev/null +++ b/console/src/main/webapp/account/css/console.css @@ -0,0 +1,31 @@ +/* Keep the labels white, even when an error occurred */ +.has-error .label { + color: white; +} + +/* Center Captcha */ +.g-recaptcha { + margin: 15px auto !important; + width: auto !important; + height: auto !important; + text-align: -webkit-center; + text-align: -moz-center; + text-align: -o-center; + text-align: -ms-center; +} + +#create_org_div { + display: none; + background-color: #eee; + border: 1px solid #ccc; + padding: 10px; + margin: 10px 0; + border-radius: 4px; +} + +.gdpr { + margin-top: 2em; +} +.org-logo { + max-width: 100%; +} diff --git a/console/src/main/webapp/account/css/select2.css b/console/src/main/webapp/account/css/select2.css new file mode 100644 index 0000000000..447b2b86cc --- /dev/null +++ b/console/src/main/webapp/account/css/select2.css @@ -0,0 +1,484 @@ +.select2-container { + box-sizing: border-box; + display: inline-block; + margin: 0; + position: relative; + vertical-align: middle; } + .select2-container .select2-selection--single { + box-sizing: border-box; + cursor: pointer; + display: block; + height: 28px; + user-select: none; + -webkit-user-select: none; } + .select2-container .select2-selection--single .select2-selection__rendered { + display: block; + padding-left: 8px; + padding-right: 20px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } + .select2-container .select2-selection--single .select2-selection__clear { + position: relative; } + .select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered { + padding-right: 8px; + padding-left: 20px; } + .select2-container .select2-selection--multiple { + box-sizing: border-box; + cursor: pointer; + display: block; + min-height: 32px; + user-select: none; + -webkit-user-select: none; } + .select2-container .select2-selection--multiple .select2-selection__rendered { + display: inline-block; + overflow: hidden; + padding-left: 8px; + text-overflow: ellipsis; + white-space: nowrap; } + .select2-container .select2-search--inline { + float: left; } + .select2-container .select2-search--inline .select2-search__field { + box-sizing: border-box; + border: none; + font-size: 100%; + margin-top: 5px; + padding: 0; } + .select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button { + -webkit-appearance: none; } + +.select2-dropdown { + background-color: white; + border: 1px solid #aaa; + border-radius: 4px; + box-sizing: border-box; + display: block; + position: absolute; + left: -100000px; + width: 100%; + z-index: 1051; } + +.select2-results { + display: block; } + +.select2-results__options { + list-style: none; + margin: 0; + padding: 0; } + +.select2-results__option { + padding: 6px; + user-select: none; + -webkit-user-select: none; } + .select2-results__option[aria-selected] { + cursor: pointer; } + +.select2-container--open .select2-dropdown { + left: 0; } + +.select2-container--open .select2-dropdown--above { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } + +.select2-container--open .select2-dropdown--below { + border-top: none; + border-top-left-radius: 0; + border-top-right-radius: 0; } + +.select2-search--dropdown { + display: block; + padding: 4px; } + .select2-search--dropdown .select2-search__field { + padding: 4px; + width: 100%; + box-sizing: border-box; } + .select2-search--dropdown .select2-search__field::-webkit-search-cancel-button { + -webkit-appearance: none; } + .select2-search--dropdown.select2-search--hide { + display: none; } + +.select2-close-mask { + border: 0; + margin: 0; + padding: 0; + display: block; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 99; + background-color: #fff; + filter: alpha(opacity=0); } + +.select2-hidden-accessible { + border: 0 !important; + clip: rect(0 0 0 0) !important; + height: 1px !important; + margin: -1px !important; + overflow: hidden !important; + padding: 0 !important; + position: absolute !important; + width: 1px !important; } + +.select2-container--default .select2-selection--single { + background-color: #fff; + border: 1px solid #aaa; + border-radius: 4px; } + .select2-container--default .select2-selection--single .select2-selection__rendered { + color: #444; + line-height: 28px; } + .select2-container--default .select2-selection--single .select2-selection__clear { + cursor: pointer; + float: right; + font-weight: bold; } + .select2-container--default .select2-selection--single .select2-selection__placeholder { + color: #999; } + .select2-container--default .select2-selection--single .select2-selection__arrow { + height: 26px; + position: absolute; + top: 1px; + right: 1px; + width: 20px; } + .select2-container--default .select2-selection--single .select2-selection__arrow b { + border-color: #888 transparent transparent transparent; + border-style: solid; + border-width: 5px 4px 0 4px; + height: 0; + left: 50%; + margin-left: -4px; + margin-top: -2px; + position: absolute; + top: 50%; + width: 0; } + +.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear { + float: left; } + +.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow { + left: 1px; + right: auto; } + +.select2-container--default.select2-container--disabled .select2-selection--single { + background-color: #eee; + cursor: default; } + .select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear { + display: none; } + +.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b { + border-color: transparent transparent #888 transparent; + border-width: 0 4px 5px 4px; } + +.select2-container--default .select2-selection--multiple { + background-color: white; + border: 1px solid #aaa; + border-radius: 4px; + cursor: text; } + .select2-container--default .select2-selection--multiple .select2-selection__rendered { + box-sizing: border-box; + list-style: none; + margin: 0; + padding: 0 5px; + width: 100%; } + .select2-container--default .select2-selection--multiple .select2-selection__rendered li { + list-style: none; } + .select2-container--default .select2-selection--multiple .select2-selection__placeholder { + color: #999; + margin-top: 5px; + float: left; } + .select2-container--default .select2-selection--multiple .select2-selection__clear { + cursor: pointer; + float: right; + font-weight: bold; + margin-top: 5px; + margin-right: 10px; } + .select2-container--default .select2-selection--multiple .select2-selection__choice { + background-color: #e4e4e4; + border: 1px solid #aaa; + border-radius: 4px; + cursor: default; + float: left; + margin-right: 5px; + margin-top: 5px; + padding: 0 5px; } + .select2-container--default .select2-selection--multiple .select2-selection__choice__remove { + color: #999; + cursor: pointer; + display: inline-block; + font-weight: bold; + margin-right: 2px; } + .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { + color: #333; } + +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline { + float: right; } + +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice { + margin-left: 5px; + margin-right: auto; } + +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { + margin-left: 2px; + margin-right: auto; } + +.select2-container--default.select2-container--focus .select2-selection--multiple { + border: solid black 1px; + outline: 0; } + +.select2-container--default.select2-container--disabled .select2-selection--multiple { + background-color: #eee; + cursor: default; } + +.select2-container--default.select2-container--disabled .select2-selection__choice__remove { + display: none; } + +.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple { + border-top-left-radius: 0; + border-top-right-radius: 0; } + +.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } + +.select2-container--default .select2-search--dropdown .select2-search__field { + border: 1px solid #aaa; } + +.select2-container--default .select2-search--inline .select2-search__field { + background: transparent; + border: none; + outline: 0; + box-shadow: none; + -webkit-appearance: textfield; } + +.select2-container--default .select2-results > .select2-results__options { + max-height: 200px; + overflow-y: auto; } + +.select2-container--default .select2-results__option[role=group] { + padding: 0; } + +.select2-container--default .select2-results__option[aria-disabled=true] { + color: #999; } + +.select2-container--default .select2-results__option[aria-selected=true] { + background-color: #ddd; } + +.select2-container--default .select2-results__option .select2-results__option { + padding-left: 1em; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__group { + padding-left: 0; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__option { + margin-left: -1em; + padding-left: 2em; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -2em; + padding-left: 3em; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -3em; + padding-left: 4em; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -4em; + padding-left: 5em; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -5em; + padding-left: 6em; } + +.select2-container--default .select2-results__option--highlighted[aria-selected] { + background-color: #5897fb; + color: white; } + +.select2-container--default .select2-results__group { + cursor: default; + display: block; + padding: 6px; } + +.select2-container--classic .select2-selection--single { + background-color: #f7f7f7; + border: 1px solid #aaa; + border-radius: 4px; + outline: 0; + background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%); + background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%); + background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); } + .select2-container--classic .select2-selection--single:focus { + border: 1px solid #5897fb; } + .select2-container--classic .select2-selection--single .select2-selection__rendered { + color: #444; + line-height: 28px; } + .select2-container--classic .select2-selection--single .select2-selection__clear { + cursor: pointer; + float: right; + font-weight: bold; + margin-right: 10px; } + .select2-container--classic .select2-selection--single .select2-selection__placeholder { + color: #999; } + .select2-container--classic .select2-selection--single .select2-selection__arrow { + background-color: #ddd; + border: none; + border-left: 1px solid #aaa; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + height: 26px; + position: absolute; + top: 1px; + right: 1px; + width: 20px; + background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%); + background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%); + background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); } + .select2-container--classic .select2-selection--single .select2-selection__arrow b { + border-color: #888 transparent transparent transparent; + border-style: solid; + border-width: 5px 4px 0 4px; + height: 0; + left: 50%; + margin-left: -4px; + margin-top: -2px; + position: absolute; + top: 50%; + width: 0; } + +.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear { + float: left; } + +.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow { + border: none; + border-right: 1px solid #aaa; + border-radius: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + left: 1px; + right: auto; } + +.select2-container--classic.select2-container--open .select2-selection--single { + border: 1px solid #5897fb; } + .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow { + background: transparent; + border: none; } + .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b { + border-color: transparent transparent #888 transparent; + border-width: 0 4px 5px 4px; } + +.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single { + border-top: none; + border-top-left-radius: 0; + border-top-right-radius: 0; + background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%); + background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%); + background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); } + +.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%); + background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%); + background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); } + +.select2-container--classic .select2-selection--multiple { + background-color: white; + border: 1px solid #aaa; + border-radius: 4px; + cursor: text; + outline: 0; } + .select2-container--classic .select2-selection--multiple:focus { + border: 1px solid #5897fb; } + .select2-container--classic .select2-selection--multiple .select2-selection__rendered { + list-style: none; + margin: 0; + padding: 0 5px; } + .select2-container--classic .select2-selection--multiple .select2-selection__clear { + display: none; } + .select2-container--classic .select2-selection--multiple .select2-selection__choice { + background-color: #e4e4e4; + border: 1px solid #aaa; + border-radius: 4px; + cursor: default; + float: left; + margin-right: 5px; + margin-top: 5px; + padding: 0 5px; } + .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove { + color: #888; + cursor: pointer; + display: inline-block; + font-weight: bold; + margin-right: 2px; } + .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover { + color: #555; } + +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice { + float: right; } + +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice { + margin-left: 5px; + margin-right: auto; } + +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { + margin-left: 2px; + margin-right: auto; } + +.select2-container--classic.select2-container--open .select2-selection--multiple { + border: 1px solid #5897fb; } + +.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple { + border-top: none; + border-top-left-radius: 0; + border-top-right-radius: 0; } + +.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } + +.select2-container--classic .select2-search--dropdown .select2-search__field { + border: 1px solid #aaa; + outline: 0; } + +.select2-container--classic .select2-search--inline .select2-search__field { + outline: 0; + box-shadow: none; } + +.select2-container--classic .select2-dropdown { + background-color: white; + border: 1px solid transparent; } + +.select2-container--classic .select2-dropdown--above { + border-bottom: none; } + +.select2-container--classic .select2-dropdown--below { + border-top: none; } + +.select2-container--classic .select2-results > .select2-results__options { + max-height: 200px; + overflow-y: auto; } + +.select2-container--classic .select2-results__option[role=group] { + padding: 0; } + +.select2-container--classic .select2-results__option[aria-disabled=true] { + color: grey; } + +.select2-container--classic .select2-results__option--highlighted[aria-selected] { + background-color: #3875d7; + color: white; } + +.select2-container--classic .select2-results__group { + cursor: default; + display: block; + padding: 6px; } + +.select2-container--classic.select2-container--open .select2-dropdown { + border-color: #5897fb; } diff --git a/ldapadmin/src/main/webapp/account/fonts/glyphicons-halflings-regular.eot b/console/src/main/webapp/account/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from ldapadmin/src/main/webapp/account/fonts/glyphicons-halflings-regular.eot rename to console/src/main/webapp/account/fonts/glyphicons-halflings-regular.eot diff --git a/ldapadmin/src/main/webapp/account/fonts/glyphicons-halflings-regular.svg b/console/src/main/webapp/account/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from ldapadmin/src/main/webapp/account/fonts/glyphicons-halflings-regular.svg rename to console/src/main/webapp/account/fonts/glyphicons-halflings-regular.svg diff --git a/ldapadmin/src/main/webapp/account/fonts/glyphicons-halflings-regular.ttf b/console/src/main/webapp/account/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from ldapadmin/src/main/webapp/account/fonts/glyphicons-halflings-regular.ttf rename to console/src/main/webapp/account/fonts/glyphicons-halflings-regular.ttf diff --git a/ldapadmin/src/main/webapp/account/fonts/glyphicons-halflings-regular.woff b/console/src/main/webapp/account/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from ldapadmin/src/main/webapp/account/fonts/glyphicons-halflings-regular.woff rename to console/src/main/webapp/account/fonts/glyphicons-halflings-regular.woff diff --git a/ldapadmin/src/main/webapp/account/js/bootstrap.js b/console/src/main/webapp/account/js/bootstrap.js similarity index 100% rename from ldapadmin/src/main/webapp/account/js/bootstrap.js rename to console/src/main/webapp/account/js/bootstrap.js diff --git a/ldapadmin/src/main/webapp/account/js/bootstrap.min.js b/console/src/main/webapp/account/js/bootstrap.min.js similarity index 100% rename from ldapadmin/src/main/webapp/account/js/bootstrap.min.js rename to console/src/main/webapp/account/js/bootstrap.min.js diff --git a/console/src/main/webapp/account/js/jquery.js b/console/src/main/webapp/account/js/jquery.js new file mode 100644 index 0000000000..d4b67f7e6c --- /dev/null +++ b/console/src/main/webapp/account/js/jquery.js @@ -0,0 +1,10308 @@ +/*! + * jQuery JavaScript Library v1.11.1 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-05-01T17:42Z + */ + +(function( global, factory ) { + + 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 inherently posses a window with a document + // (such as Node.js), expose a jQuery-making 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 ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +// + +var deletedIds = []; + +var slice = deletedIds.slice; + +var concat = deletedIds.concat; + +var push = deletedIds.push; + +var indexOf = deletedIds.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + version = "1.11.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.1, IE<9 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // 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 num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // 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; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + 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(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: deletedIds.sort, + splice: deletedIds.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, 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" && !jQuery.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 = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.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() {}, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( support.ownLast ) { + for ( key in obj ) { + return hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Support: Android<4.1, IE<9 + 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 ) { + var len; + + if ( arr ) { + if ( indexOf ) { + return indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + while ( j < len ) { + first[ i++ ] = second[ j++ ]; + } + + // Support: IE<9 + // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) + if ( len !== len ) { + while ( second[j] !== undefined ) { + 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 value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArray ) { + 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, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + 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 ( !jQuery.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; + }, + + now: function() { + return +( new Date() ); + }, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v1.10.19 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-04-18 + */ +(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" + -(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; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[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 + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // 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 + 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( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "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 = /[+~]/, + rescape = /'|\\/g, + + // 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 ); + }; + +// 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 match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + 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 div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = 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 = attrs.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 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // 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 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 !== strundefined && 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, + doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", function() { + setDocument(); + }, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", function() { + setDocument(); + }); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { + div.innerHTML = "
"; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + 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 !== strundefined && 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 http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // 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 + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // 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 + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowclip^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // 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 ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.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 ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[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 does not implement inclusive descendent + // 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 === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( 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 === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( 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 doc; +}; + +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 && + ( !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.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 !== strundefined && 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 + " " ).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, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + 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 + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && 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 ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // 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 ) { + (node[ expando ] || (node[ expando ] = {}))[ 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.call( 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 ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + 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": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === 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, + checkNonElements = base && dir === "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 ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir 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 ] = {}); + if ( (oldCache = outerCache[ dir ]) && + 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 + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +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.call( 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.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + 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; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, 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 ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + 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 no seed and only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && 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, + 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 +// 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( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.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( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.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( div ) { + return div.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; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; + }); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + 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; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return 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, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( 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; + + // 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 ( jQuery.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] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).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.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + 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.extend({ + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +jQuery.fn.extend({ + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.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.unique( matched ) : matched ); + }, + + // Determine the position of an element within + // the matched set of elements + 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 jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.unique( + 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 ) { + do { + cur = cur[ dir ]; + } while ( cur && 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 jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.unique( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +}); +var rnotwhite = (/\S+/g); + + + +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( rnotwhite ) || [], 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" ? + ( optionsCache[ options ] || 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, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + 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 && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + 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; +}; + + +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + 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(); + }, + // 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 = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + 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( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + + } else if ( !(--remaining) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + 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, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // 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; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // 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 ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); + +/** + * Clean-up method for dom ready events + */ +function detach() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } +} + +/** + * The ready event handler and self cleanup method + */ +function completed() { + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + + +var strundefined = typeof undefined; + + + +// Support: IE<9 +// Iteration over object's inherited properties before its own +var i; +for ( i in jQuery( support ) ) { + break; +} +support.ownLast = i !== "0"; + +// Note: most support tests are defined in their respective modules. +// false until the test is run +support.inlineBlockNeedsLayout = false; + +// Execute ASAP in case we need to set body.style.zoom +jQuery(function() { + // Minified: var a,b,c,d + var val, div, body, container; + + body = document.getElementsByTagName( "body" )[ 0 ]; + if ( !body || !body.style ) { + // Return for frameset docs that don't have a body + return; + } + + // Setup + div = document.createElement( "div" ); + container = document.createElement( "div" ); + container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; + body.appendChild( container ).appendChild( div ); + + if ( typeof div.style.zoom !== strundefined ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; + + support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; + if ( val ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); +}); + + + + +(function() { + var div = document.createElement( "div" ); + + // Execute the test only if not already executed in another module. + if (support.deleteExpando == null) { + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +})(); + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( elem ) { + var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], + nodeType = +elem.nodeType || 1; + + // Do not set data on non-element DOM nodes because it will not be cleared (#8335). + return nodeType !== 1 && nodeType !== 9 ? + false : + + // Nodes accept data unless otherwise specified; rejection can be conditional + !noData || noData !== true && elem.getAttribute("classid") === noData; +}; + + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + +function internalData( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // The following elements (space-suffixed to avoid Object.prototype collisions) + // throw uncatchable exceptions if you attempt to set expando properties + noData: { + "applet ": true, + "embed ": true, + // ...but Flash objects (which have this classid) *can* handle expandos + "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var i, name, data, + elem = this[0], + attrs = elem && elem.attributes; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return arguments.length > 1 ? + + // Sets one value + this.each(function() { + jQuery.data( this, key, value ); + }) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + + +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( 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 intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, 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 = jQuery._data( 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 cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + }; + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.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 < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; +}; +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + // Minified: var a,b,c + var input = document.createElement( "input" ), + div = document.createElement( "div" ), + fragment = document.createDocumentFragment(); + + // Setup + div.innerHTML = "
a"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName( "tbody" ).length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = + document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + input.type = "checkbox"; + input.checked = true; + fragment.appendChild( input ); + support.appendChecked = input.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE6-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // #11217 - WebKit loses check when the name is after the checked attribute + fragment.appendChild( div ); + div.innerHTML = ""; + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + support.noCloneEvent = true; + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Execute the test only if not already executed in another module. + if (support.deleteExpando == null) { + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + } +})(); + + +(function() { + var i, eventName, + div = document.createElement( "div" ); + + // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event) + for ( i in { submit: true, change: true, focusin: true }) { + eventName = "on" + i; + + if ( !(support[ i + "Bubbles" ] = eventName in window) ) { + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + div.setAttribute( eventName, "t" ); + support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +})(); + + +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * 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 tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( 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; + } + + // 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 !== strundefined && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + 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/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + 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; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + 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 the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = 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(".") >= 0 ) { + // 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.namespace_re = 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 && !jQuery.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() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && jQuery.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) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.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; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( jQuery._data( 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; + 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.namespace_re || event.namespace_re.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 sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + 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 ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + 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 ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.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; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, 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: IE < 9, Android < 4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // 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 || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +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 mousenter/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; + } + }; +}); + +// IE submit delegation +if ( !support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !support.focusinBubbles ) { + 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 ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + jQuery._removeData( doc, fix ); + } else { + jQuery._data( doc, fix, attaches ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // 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 ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + 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 this; + } + + 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 this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( 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 ); + }); + }, + + 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 ); + } + } +}); + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!support.noCloneEvent || !support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, 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; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + 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 || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[1] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.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 ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + 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 ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + deletedIds.push( id ); + } + } + } + } + } +}); + +jQuery.fn.extend({ + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( 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 this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + 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 ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + 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 arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + 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 ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[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 || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + + // Use of this method is a temporary fix (more like optmization) until something better comes along, + // since it was removed from specification and supported only in FF + style.display : jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = (iframe || jQuery( " + +
+ +
+ +
+ {{ 'index.' + profile | translate}} +
+ + + + + +
+ + + + + + + diff --git a/console/src/main/webapp/manager/app/assets/lang/de.json b/console/src/main/webapp/manager/app/assets/lang/de.json new file mode 100644 index 0000000000..f4f40fdb1f --- /dev/null +++ b/console/src/main/webapp/manager/app/assets/lang/de.json @@ -0,0 +1,290 @@ +{ + "nav.dashboard" : "Dashboard", + "nav.users" : "Benutzer", + "nav.orgs" : "Organisationen", + "nav.analytics" : "Statistiken", + "nav.logs" : "Protokolle", + "nav.roles" : "Rollen", + "nav.delegations" : "Delegationen", + "index.SUPERUSER" : "Administrator", + "index.DELEGATED" : "Delegation", + "index.SUPERUSERtip" : "Mit Ihrer Rolle können Sie alle Konten bearbeiten", + "index.DELEGATEDtip" : "Ihr Konto kann Benutzer einer Auswahl von Organisationen verwalten", + "home.users" : "Benutzer verwalten", + "home.waiting_users" : "wartende Benutzer", + "home.waiting_user" : "wartende Benutzer", + "home.expired_users" : "abgelaufene Benutzer heute", + "home.expired_user" : "abgelaufene Benutzer heute", + "home.connected_users" : "Web-Services Benutzer heute", + "home.connected_user" : "Web-Services Benutzer heute", + "home.connected_users_none" : "Kein registrierter Benutzer von Web-Services heute!", + "home.view_manage" : "Anzeigen und Verwalten", + "home.nb_requests" : "Anf.", + "home.user" : "Benutzer", + "home.org" : "Org.", + "home.logs" : "Administrationsprotokoll", + "tab.infos" : "Infos", + "tab.roles" : "Rollen", + "tab.delegations" : "Delegationen", + "tab.analytics" : "Statistiken", + "tab.messages" : "Nachrichten", + "tab.logs" : "Protokolle", + "tab.manage" : "Verwalten", + "tab.area" : "Bereich", + "tab.users" : "Mitglieder", + "user.userlist" : "Alle Benutzer", + "user.updated" : "Benutzer aktualisiert", + "user.deleted" : "Benutzer gelöscht", + "user.created" : "Benutzer erstellt", + "user.error" : "Fehler beim Erstellen des Benutzers", + "user.save" : "Speichern", + "user.delete" : "Benutzer löschen", + "user.new_user" : "Neuer Benutzer", + "user.discard" : "Änderungen verwerfen", + "user.login" : "Login", + "user.cn" : "Vollständiger Name", + "user.mail" : "E-Mail", + "user.address" : "Adresse", + "user.postalAddress" : "Adresse", + "user.org" : "Organisation", + "user.description" : "Beschreibung", + "user.manager" : "Vorgesetzter", + "user.expire" : "Ablaufdatum", + "user.reset" : "Zurücksetzen", + "user.password" : "Passwort", + "user.sn" : "Nachname", + "user.gn" : "Vorname", + "user.givenName" : "Vorname", + "user.phone" : "Telefon", + "user.facsimileTelephoneNumber" : "Fax", + "user.fax" : "Fax", + "user.title" : "Titel", + "user.privacyPolicyAgreementDate" : "Date of agreement to conditions", + "user.warning" : "Achtung, diese Aktion kann nicht rückgängig gemacht werden — ", + "user.pendingmsg" : "Ausstehende Benutzer ", + "user.expiredmsg" : "Benutzer abgelaufen seit", + "user.confirm" : "Bestätigen", + "user.orgFirst" : "Zuerst Organisation bestätigen : ", + "user.hasdeleg" : "Dieser Benutzer hat eine Administrator-Delegation", + "user.nodeleg" : "Keine Administrator-Delegation für diesen Benutzer", + "user.manages_roles" : "Rollen verwalten :", + "user.manages_orgs" : "Unter Benutzern, die Organisationen angehören:", + "user.select_role" : "Wählen Sie eine Rolle aus", + "user.action" : "Aktion", + "user.remove" : "Entfernen", + "user.logs" : "Benutzerprotokolle", + "user.shadowExpire" : "Auslaufdatum", + "user.preferredLanguage": "Default language", + "user.telephoneNumber" : "Telefon", + "user.uid" : "Kennung", + "user.note" : "Interne Notizen", + "orgs.title_plural" : "Organisationen", + "orgs.title_one" : "Organisation", + "orgs.title_none" : "Keine Organisation", + "orgs.allorgs" : "Alle", + "orgs.pending" : "Ausstehend", + "orgs.neworg" : "Neue Organisation", + "orgs.filter_orgs" : "Filter", + "org.name" : "Name", + "org.shortName" : "Kurzname", + "org.type" : "Typ", + "org.address" : "Adresse", + "org.save" : "Speichern", + "org.updated" : "Organisation aktualisiert", + "org.deleted" : "Organisation gelöscht", + "org.created" : "Organisation erstellt", + "org.error" : "Fehler beim Erstellen der Organisation", + "org.warning" : "Achtung, diese Aktion kann nicht rückgängig gemacht werden — ", + "org.delete" : "Organisation löschen", + "org.orglist" : "Organisationen", + "org.pendingmsg" : "Ausstehende Organisationen ", + "org.confirm" : "Bestätigen", + "org.select" : "Organisation auswählen", + "org.membersCount" : "Mitglieder", + "org.ohasdeleg" : "Benutzer dieser Organisation können von folgenden Benutzern verwaltet werden:", + "org.userremoved" : "Der Benutzer wurde aus der Organisation entfernt.", + "org.useradded" : "Der Benutzer wurde der Organisation hinzugefügt.", + "org.userlabel" : "Hinzufügen", + "org.description" : "Beschreibung", + "org.url" : "Webseite", + "org.logo" : "Logo", + "org.note" : "Aufzeichnungen", + "org.cities" : "Bereich", + "org.orgType" : "Organisationstyp", + "imageinput.label" : "Logo laden", + "roles.title_plural" : "Rollen", + "roles.title_one" : "Rolle", + "roles.title_none" : "Keine Rolle", + "roles.allroles" : "Alle Rollen", + "roles.newrole" : "Neue Rolle", + "roles.filter_roles" : "Filter", + "role.save" : "Speichern", + "role.updated" : "Rolle aktualisiert", + "role.deleted" : "Rolle gelöscht", + "role.created" : "Rolle erstellt", + "role.error" : "Fehler beim Erstellen der Rolle", + "role.warning" : "Achtung, diese Aktion kann nicht rückgängig gemacht werden — ", + "role.delete" : "Role löschen", + "role.rolelist" : "Alle Rollen", + "role.confirm" : "Bestätigen", + "role.select" : "Wähle eine Rolle", + "role.membersCount" : "Mitglieder", + "role.description" : "Beschreibung", + "role.cn" : "Name", + "role.favorite" : "Favorit", + "role.favorites" : "Favoriten", + "role.useradded" : "Der Benutzer wurde der Rolle hinzugefügt.", + "role.userremoved" : "Der Benutzer wurde aus der Rolle entfernt.", + "role.userlabel" : "Hinzufügen", + "role.helpFormat" : "Der Bezeichner darf nur alphanumerische Großbuchstaben Zeichen enthalten.", + "delegations.title" : "Management-Delegation", + "delegations.title_plural" : "Management-Delegationen", + "delegations.title_no_delegations" : "Keine Managementdelegation", + "delegations.filter" : "Filter", + "delegations.delete" : "Löschen", + "delegation.dupdated" : "Delegation aktualisiert", + "delegation.ddeleted" : "Delegation gelöscht", + "delegation.derror" : "Fehler beim Aktualisieren Delegation", + "delegation.restricted" : "Das Hinzufügen einer Delegation zu einem SUPERUSER ist nicht zulässig.", + "area.save" : "Bereich speichern", + "area.updated" : "Bereich aktualisiert", + "area.error" : "Fehler beim Speichern des Bereichs", + "area.selectByBBOX" : "Rechteckauswahl", + "area.selection" : "Auswählen", + "area.area" : "Objekte", + "area.all" : "Alle", + "area.none" : "Keine", + "area.search" : "Suchen", + "area.import" : "CSV importieren", + "area.export" : "CSV exportieren", + "role.allusers" : "Alle Benutzer", + "role.system" : "geOrchestra-Rollen", + "role.app" : "Andere Rollen", + "role.newuser" : "Neuer Benutzer", + "role.newrole" : "Neue Rolle", + "role.user" : "Benutzer", + "role.users" : "Benutzer", + "role.users_none" : "Keine Benutzer", + "role.filter_users" : "Filter", + "role.created" : "Rolle erstellt", + "role.updated" : "Rolle aktualisiert", + "role.deleted" : "Rolle gelöscht", + "role.error" : "Fehler beim Erstellen der Rolle", + "role.deleteError" : "Fehler beim Löschen der Rolle", + "role.search" : "Rolle suchen", + "role.all" : "Alle", + "role.delete" : "Löschen", + "users.createRole" : "Rolle erstellen", + "users.newRole" : "Name", + "users.newRoleName" : "Rollennname", + "users.newRoleDesc" : "Beschreibung", + "users.cancel" : "Abbrechen", + "users.save" : "Speichern", + "users.delete" : "Löschen", + "users.confirm" : "Bestätigen", + "users.confirmText" : "Wollen Sie diese Rolle wirklich löschen", + "users.roleUpdated" : "Rolle aktualisiert", + "users.roleUpdateError" : "Fehler beim Zuweisen der Rolle", + "users.user" : "Benutzer", + "users.login" : "Login", + "users.organization" : "Organisation", + "users.email" : "E-Mail", + "users.pending_desc" : "Benutzer warten auf Validierung", + "users.filter_selected" : "Nur Auswahl anzeigen", + "users.PENDING" : "Ausstehende Benutzer", + "users.ORGADMIN" : "Delegierten", + "users.TEMPORARY" : "Temporäre", + "users.EXPIRED" : "Abgelaufen", + "users.SUPERUSER" : "Kontoadministratoren", + "users.ADMINISTRATOR" : "Datenadministratoren", + "users.GN_ADMIN" : "Metadatenadministratoren", + "users.GN_EDITOR" : "Metadaten-Editoren", + "users.GN_REVIEWER" : "Metadaten-Prüfer", + "users.EXTRACTORAPP" : "Datenextraktion", + "users.USER" : "Benutzer", + "users.REFERENT" : "Organisations-Referenten", + "sel.all" : "Alle", + "sel.none" : "Keine", + "sel.export_csv" : "Export CSV", + "sel.export_vcard" : "Export VCard", + "msg.compose" : "Nachricht erstellen", + "msg.date" : "Datum", + "msg.subject" : "Betreff", + "msg.nosubject" : "— kein Betreff —", + "msg.sender" : "Absender", + "msg.messages" : "Nachrichten", + "msg.on" : "am", + "msg.attachments" : "Anhänge", + "msg.templates" : "Vorlage auswählen", + "msg.title" : "Titel", + "msg.content" : "Inhalt", + "msg.cancel" : "Abbrechen", + "msg.send" : "Absenden", + "msg.sent" : "Nachricht abgesendet", + "msg.error" : "Fehler beim Versenden der Nachricht", + "msg.empty" : "Keine Nachrichten", + "logs.title" : "Administrationsprotokoll", + "logs.logs" : "Administrationsprotokoll", + "logs.date" : "Datum", + "logs.sender" : "Autor", + "logs.target" : "Empfänger", + "logs.type" : "Aktion", + "logs.target" : "Ziel", + "logs.allsender" : "Alle", + "logs.alltype" : "Alle", + "logs.alltarget" : "Alle", + "logs.error" : "Fehler beim Laden der Daten", + "logs.target" : "Änderung von", + "logs.modification" : "Änderung des Attributs", + "logs.replace" : "Ersetzt", + "logs.to" : "durch", + "logs.clear" : "Löscht den Wert", + "logs.set" : "Mehrwert schaffen", + "logs.all" : "Logs", + "logs.added" : "Elemente hinzugefügt", + "logs.removed" : "Elemente gelöscht​", + "logs.pendingusercreated" : "Ausstehender Benutzer erstellt", + "logs.pendinguserrefused" : "Ausstehender Benutzer abgelehnt", + "logs.pendinguseraccepted" : "Ausstehender Benutzer akzeptiert", + "logs.pendingorgcreated" : "Ausstehende Organisation erstellt", + "logs.pendingorgrefused" : "Ausstehende Organisation abgelehnt", + "logs.pendingorgaccepted" : "Ausstehende Organisation akzeptiert", + "logs.roledeleted" : "Rolle gelöscht", + "logs.orgcreated" : "Organisation erstellt", + "logs.orgdeleted" : "Organisation gelöscht", + "logs.userpasswordchanged": "Passwort geändert", + "logs.usercreated" : "Benutzer erstellt", + "logs.userdeleted" : "Benutzer gelöscht", + "logs.system" : "Systemrolle", + "logs.custom" : "Benutzerdefinierte Rolle", + "logs.roleadded" : "zum Benutzer hinzugefügt", + "logs.roleremoved" : "vom Benutzer entfernt", + "logs.emailrecoverysent": "E-Mail zur Passwortwiederherstellung gesendet", + "logs.rolecreated" : "Rolle erstellt", + "analytics.title" : "Statistiken", + "analytics.all" : "Alle", + "analytics.requests" : "Anzahl der Anfragen", + "analytics.layers" : "Meistgesehene Ebenen", + "analytics.errorload" : "Fehler beim Laden der Daten", + "analytics.nodata" : "Keine Daten für diesen Zeitraum", + "analytics.graphview" : "Kurvenansicht", + "analytics.dataview" : "Tabellenansicht", + "analytics.DAY" : "täglich", + "analytics.WEEK" : "wöchentlich", + "analytics.MONTH" : "monatlich", + "analytics.HOUR" : "stündlich", + "analytics.extractions" : "Ebenenauszüge", + "analytics.saveAsPNG" : "Als PNG herunterladen", + "analytics.saveAsCSV" : "Als CSV herunterladen", + "date.day" : "Heute", + "date.week" : "Letzte Woche", + "date.month" : "Letzer Monat", + "date.3month" : "Letzte 3 Monate", + "date.year" : "Letztes Jahr", + "date.custom" : "benutzerdefinierter Zeitraum", + "date.to" : "bis", + "sasl.remote.user" : "Remote-Benutzer auf", + "sasl.tooltip" : "In diesem Feld wird der Benutzername auf dem Remote-System definiert. Füllen Sie dieses Feld aus, um die Remote-Benutzerauthentifizierung zu aktivieren. Deaktivieren Sie das Feld für die lokale Authentifizierung (eine Kennwortregenerierung ist erforderlich).", + "editUserDetailsForm.deleteConfirm" : "Möchten Sie Ihr Konto wirklich löschen?", + "editUserDetailsForm.deleteFail" : "Konto konnte nicht gelöscht werden" +} diff --git a/console/src/main/webapp/manager/app/assets/lang/en.json b/console/src/main/webapp/manager/app/assets/lang/en.json new file mode 100644 index 0000000000..15143a3e1c --- /dev/null +++ b/console/src/main/webapp/manager/app/assets/lang/en.json @@ -0,0 +1,290 @@ +{ + "nav.dashboard" : "Dashboard", + "nav.users" : "Users", + "nav.orgs" : "Organizations", + "nav.analytics" : "Analytics", + "nav.logs" : "Logs", + "nav.roles" : "Roles", + "nav.delegations" : "Delegations", + "index.SUPERUSER" : "Administrator", + "index.DELEGATED" : "Delegation", + "index.SUPERUSERtip" : "Your role allows you to manage all accounts", + "index.DELEGATEDtip" : "Your account is allowed to manage users in a selection of Orgs", + "home.users" : "Manage users", + "home.waiting_users" : "waiting users", + "home.waiting_user" : "waiting user", + "home.expired_users" : "users expired today", + "home.expired_user" : "user expired today", + "home.connected_users" : "users of services today", + "home.connected_user" : "user of services today", + "home.connected_users_none" : "No registered user of services today !", + "home.view_manage" : "View & manage", + "home.nb_requests" : "Req.", + "home.user" : "User", + "home.org" : "Org.", + "home.logs" : "Admin logs", + "tab.infos" : "Infos", + "tab.roles" : "Roles", + "tab.delegations" : "Delegations", + "tab.analytics" : "Analytics", + "tab.messages" : "Messages", + "tab.logs" : "Logs", + "tab.manage" : "Manage", + "tab.area" : "Area", + "tab.users" : "Members", + "user.userlist" : "All users", + "user.updated" : "User updated", + "user.deleted" : "User deleted", + "user.created" : "User created", + "user.error" : "Error while saving user", + "user.save" : "Save", + "user.delete" : "Delete user", + "user.new_user" : "New user", + "user.discard" : "Cancel", + "user.login" : "Login", + "user.cn" : "Full name", + "user.mail" : "E-mail", + "user.address" : "Address", + "user.postalAddress" : "Address", + "user.org" : "Organization", + "user.description" : "Description", + "user.manager" : "Manager", + "user.expire" : "Expire", + "user.reset" : "Reset", + "user.password" : "Local Password", + "user.sn" : "Second Name", + "user.gn" : "First Name", + "user.givenName" : "First Name", + "user.phone" : "Phone", + "user.facsimileTelephoneNumber" : "Fax", + "user.fax" : "Fax", + "user.title" : "Job title", + "user.privacyPolicyAgreementDate" : "Date of agreement to conditions", + "user.warning" : "Warning, this action is irreversible — ", + "user.pendingmsg" : "Pending user ", + "user.expiredmsg" : "Expired user since", + "user.confirm" : "Confirm", + "user.orgFirst" : "Confirm organization first : ", + "user.hasdeleg" : "This user holds an admin delegation", + "user.nodeleg" : "No admin delegation for this user", + "user.manages_roles" : "Manage roles:", + "user.manages_orgs" : "Among users belonging to organizations:", + "user.select_role" : "Select a role", + "user.action" : "Action", + "user.remove" : "Remove", + "user.logs" : "User logs", + "user.shadowExpire" : "Expiration date", + "user.preferredLanguage": "Default language", + "user.telephoneNumber" : "Phone number", + "user.uid" : "Identifier", + "user.note" : "Internal notes", + "orgs.title_plural" : "organizations", + "orgs.title_one" : "organization", + "orgs.title_none" : "No organization", + "orgs.allorgs" : "All orgs", + "orgs.pending" : "Pending", + "orgs.neworg" : "New org", + "orgs.filter_orgs" : "Filter", + "org.name" : "Name", + "org.shortName" : "Short name", + "org.type" : "Type", + "org.address" : "Address", + "org.save" : "Save", + "org.updated" : "Organization updated", + "org.deleted" : "Organization deleted", + "org.created" : "Organization created", + "org.error" : "Error while saving organization", + "org.warning" : "Warning, this action is irreversible — ", + "org.delete" : "Delete organization", + "org.orglist" : "All orgs", + "org.pendingmsg" : "Pending organization", + "org.confirm" : "Confirm", + "org.select" : "Select an organization", + "org.membersCount" : "Members count", + "org.ohasdeleg" : "Users belonging to this Org can be managed by:", + "org.userremoved" : "User has been removed from organization", + "org.useradded" : "User has been added to organization", + "org.userlabel" : "Add", + "org.description" : "Description", + "org.url" : "Website", + "org.logo" : "Logo", + "org.note" : "Internal Notes", + "org.cities" : "Area", + "org.orgType" : "Organisation type", + "imageinput.label" : "Upload logo", + "roles.title_plural" : "roles", + "roles.title_one" : "role", + "roles.title_none" : "No role", + "roles.allroles" : "All roles", + "roles.newrole" : "New role", + "roles.filter_roles" : "Filter", + "role.save" : "Save", + "role.updated" : "Role has been updated", + "role.deleted" : "Role has been deleted", + "role.created" : "Role has been created", + "role.error" : "Error while creating role", + "role.warning" : "Warning, this action cannot be undone — ", + "role.delete" : "Delete role", + "role.rolelist" : "All roles", + "role.confirm" : "Confirm", + "role.select" : "Pick a role", + "role.membersCount" : "Members", + "role.description" : "Description", + "role.cn" : "Id", + "role.favorite" : "Favorite", + "role.favorites" : "Favorites", + "role.useradded" : "User has been added to role", + "role.userremoved" : "User has been removed from role", + "role.userlabel" : "Add", + "role.helpFormat" : "Identifier should only contain uppercased alphanumerical characters", + "delegations.title" : "management delegation", + "delegations.title_plural" : "management delegations", + "delegations.title_no_delegations" : "No management delegation", + "delegations.filter" : "Filter", + "delegations.delete" : "Delete", + "delegation.dupdated" : "Delegation updated", + "delegation.ddeleted" : "Delegation deleted", + "delegation.derror" : "Error while updating delegation", + "delegation.restricted" : "Adding a delegation to a SUPERUSER is not allowed.", + "area.save" : "Save area", + "area.updated" : "Area updated", + "area.error" : "Error saving area", + "area.selectByBBOX" : "Select by Bounding Box", + "area.selection" : "Selection", + "area.area" : "Area(s)", + "area.all" : "All", + "area.none" : "None", + "area.search" : "Search", + "area.import" : "CSV Import", + "area.export" : "CSV Export", + "role.allusers" : "All users", + "role.system" : "geOrchestra roles", + "role.app" : "Other roles", + "role.newuser" : "New user", + "role.newrole" : "New role", + "role.user" : "user", + "role.users" : "users", + "role.users_none" : "No user", + "role.filter_users" : "Filter", + "role.created" : "Role created", + "role.updated" : "Role updated", + "role.deleted" : "Role deleted", + "role.error" : "Error creating role", + "role.deleteError" : "Error deleting role", + "role.search" : "Search role", + "role.all" : "All", + "role.delete" : "Delete", + "users.createRole" : "Create role", + "users.newRole" : "Name", + "users.newRoleName" : "Role name", + "users.newRoleDesc" : "Description", + "users.cancel" : "Cancel", + "users.save" : "Save", + "users.delete" : "Delete", + "users.confirm" : "Confirm", + "users.confirmText" : "Do you really want to delete role", + "users.roleUpdated" : "Role updated", + "users.roleUpdateError" : "Error associating to roles", + "users.user" : "User", + "users.login" : "Login", + "users.organization" : "Organization", + "users.email" : "Mail", + "users.pending_desc" : "Users waiting for validation", + "users.filter_selected" : "View selected only", + "users.PENDING" : "Waiting validation", + "users.ORGADMIN" : "Delegates", + "users.TEMPORARY" : "Temporary", + "users.EXPIRED" : "Expired", + "users.SUPERUSER" : "Accounts administrators", + "users.ADMINISTRATOR" : "Data administrators", + "users.GN_ADMIN" : "Metadata administrators", + "users.GN_EDITOR" : "Metadata editors", + "users.GN_REVIEWER" : "Metadata reviewers", + "users.EXTRACTORAPP" : "Data extractor", + "users.USER" : "Users", + "users.REFERENT" : "Organization referent", + "sel.all" : "All", + "sel.none" : "None", + "sel.export_csv" : "CSV Export", + "sel.export_vcard" : "VCard Export", + "msg.compose" : "Compose message", + "msg.date" : "Date", + "msg.subject" : "Subject", + "msg.nosubject" : "— no subject —", + "msg.sender" : "Sender", + "msg.messages" : "Messages", + "msg.on" : "on", + "msg.attachments" : "Attachments", + "msg.templates" : "Choose template", + "msg.title" : "Title", + "msg.content" : "Content", + "msg.cancel" : "Cancel", + "msg.send" : "Send", + "msg.sent" : "Message sent!", + "msg.error" : "Error sending message", + "msg.empty" : "No mails", + "logs.title" : "Administration logs", + "logs.logs" : "Admin logs", + "logs.date" : "Date", + "logs.sender" : "Author", + "logs.target" : "Recipient", + "logs.type" : "Action", + "logs.target" : "Target", + "logs.allsender" : "All", + "logs.alltype" : "All", + "logs.alltarget" : "All", + "logs.error" : "Error while loading data", + "logs.target" : "Modification of", + "logs.modification" : "Modification on the attribute", + "logs.replace" : "Replace the value", + "logs.to" : "by", + "logs.clear" : "Clear the value", + "logs.set" : "Set the value", + "logs.all" : "Logs", + "logs.added" : "Item(s) added", + "logs.removed" : "item(s) removed ", + "logs.pendingusercreated" : "Pending user created", + "logs.pendinguserrefused" : "Pending user refused", + "logs.pendinguseraccepted" : "Pending user accepted", + "logs.pendingorgcreated" : "Pending org created", + "logs.pendingorgrefused" : "Pending org refused", + "logs.pendingorgaccepted" : "Pending org accepted", + "logs.roledeleted" : "Role deleted", + "logs.orgcreated" : "Organisation created", + "logs.orgdeleted" : "Organisation deleted", + "logs.userpasswordchanged": "Password changed", + "logs.usercreated" : "User created", + "logs.userdeleted" : "User deleted", + "logs.system" : "System role", + "logs.custom" : "Custom role", + "logs.roleadded" : "added to user", + "logs.roleremoved" : "removed from this user", + "logs.emailrecoverysent": "Password recovery email sent", + "logs.rolecreated" : "Role created", + "analytics.title" : "Analytics", + "analytics.all" : "Everyone", + "analytics.requests" : "Number of requests", + "analytics.layers" : "Most viewed layers", + "analytics.errorload" : "Error loading data", + "analytics.nodata" : "No data for this period", + "analytics.graphview" : "View as graph", + "analytics.dataview" : "View as table", + "analytics.DAY" : "per day", + "analytics.WEEK" : "per week", + "analytics.MONTH" : "per month", + "analytics.HOUR" : "per hour", + "analytics.extractions" : "Layers extractions", + "analytics.saveAsPNG" : "Save as PNG", + "analytics.saveAsCSV" : "Save as CSV", + "date.day" : "Today", + "date.week" : "Last week", + "date.month" : "Last month", + "date.3month" : "Last 3 months", + "date.year" : "Last year", + "date.custom" : "Custom range", + "date.to" : "to", + "sasl.remote.user" : "Remote user on", + "sasl.tooltip" : "This field is used to define the user name on the remote system. Fill in this field to enable remote user authentication. Clear the field for local authentication (password regeneration will be required).", + "editUserDetailsForm.deleteConfirm" : "Do you really want to delete your account?", + "editUserDetailsForm.deleteFail" : "Failed to delete account" +} diff --git a/console/src/main/webapp/manager/app/assets/lang/es.json b/console/src/main/webapp/manager/app/assets/lang/es.json new file mode 100644 index 0000000000..61703273a2 --- /dev/null +++ b/console/src/main/webapp/manager/app/assets/lang/es.json @@ -0,0 +1,290 @@ +{ + "nav.dashboard" : "Cuadro de mando", + "nav.users" : "Usuarios", + "nav.orgs" : "Organizaciones", + "nav.analytics" : "Estadísticas", + "nav.logs" : "Logs", + "nav.roles" : "Roles", + "nav.delegations" : "Delegaciones", + "index.SUPERUSER" : "Administrador", + "index.DELEGATED" : "Delegación", + "index.SUPERUSERtip" : "Tu función te permite editar todas las cuentas", + "index.DELEGATEDtip" : "Su cuenta puede gestionar usuarios de una selección de organizaciones", + "home.users" : "Administrar los usuarios", + "home.waiting_users" : "usuarios en espera", + "home.waiting_user" : "usuario en espera", + "home.expired_users" : "usuarios expirados hoy", + "home.expired_user" : "usuario expirados hoy", + "home.connected_users" : "usuarios de servicios hoy", + "home.connected_user" : "usuario de servicios hoy", + "home.connected_users_none" : "¡Ningún usuario de servicios registrado hoy!", + "home.view_manage" : "Ver y administrar", + "home.nb_requests" : "Consultas", + "home.user" : "Usuarios", + "home.org" : "Organización", + "home.logs" : "Logs de administración", + "tab.infos" : "Informaciones", + "tab.roles" : "Roles", + "tab.delegations" : "Delegaciones", + "tab.analytics" : "Estadísticas", + "tab.messages" : "Mensajes", + "tab.logs" : "Logs", + "tab.manage" : "Administrar", + "tab.area" : "Zona de competencia", + "tab.users" : "Miembros", + "user.userlist" : "Todos los usuarios", + "user.updated" : "Usuario modificado", + "user.deleted" : "Usuario eliminado", + "user.created" : "Usuario creado", + "user.error" : "Error al crear el usuario", + "user.save" : "Guardar", + "user.delete" : "Eliminar el usuario", + "user.new_user" : "Nuevo usuario", + "user.discard" : "Cancelar", + "user.login" : "Identificador", + "user.cn" : "Nombre completo", + "user.mail" : "Correo electrónico", + "user.address" : "Dirección", + "user.postalAddress" : "Dirección", + "user.org" : "Organización", + "user.description" : "Descripción", + "user.manager" : "Responsable", + "user.expire" : "Fecha de expiración", + "user.reset" : "Volver a generar", + "user.password" : "Contraseña", + "user.sn" : "Apellidos", + "user.gn" : "Nombres", + "user.givenName" : "Nombres", + "user.phone" : "Teléfono", + "user.facsimileTelephoneNumber" : "Fax", + "user.fax" : "Fax", + "user.title" : "Puesto", + "user.privacyPolicyAgreementDate" : "Fecha de aceptación de las condiciones", + "user.warning" : "Cuidado, esta acción no se puede deshacer — ", + "user.pendingmsg" : "Usuario en espera de confirmación ", + "user.expiredmsg" : "Usuario expirado desde", + "user.confirm" : "Confirmar", + "user.orgFirst" : "Primero confirmar la organización: ", + "user.hasdeleg" : "Se asignó una delegación de administración a este usuario", + "user.nodeleg" : "No se asignó una delegación de administración a este usuario", + "user.manages_roles" : "Administra la asignación de los siguientes roles:", + "user.manages_orgs" : "Parte de los usuarios miembros de las siguientes organizaciones:", + "user.select_role" : "Seleccionar un rol", + "user.action" : "Operación", + "user.remove" : "Eliminar", + "user.logs" : "Logs de usuarios", + "user.shadowExpire" : "Fecha de expiración", + "user.preferredLanguage": "Idioma predeterminado", + "user.telephoneNumber" : "Teléfono", + "user.uid" : "Identificador", + "user.note" : "Interno notas", + "orgs.title_plural" : "organizaciones", + "orgs.title_one" : "organización", + "orgs.title_none" : "Ninguna organización", + "orgs.allorgs" : "Todas", + "orgs.pending" : "En espera", + "orgs.neworg" : "Nueva organización", + "orgs.filter_orgs" : "Filtrar", + "org.name" : "Nombre", + "org.shortName" : "Sigla o nombre corto", + "org.type" : "Tipo de organización", + "org.address" : "Dirección", + "org.save" : "Guardar", + "org.updated" : "Organización modificada", + "org.deleted" : "Organización eliminada", + "org.created" : "Organización creada", + "org.error" : "Error al crear la organización", + "org.warning" : "Cuidado, esta acción no se puede deshacer — ", + "org.delete" : "Eliminar la organización", + "org.orglist" : "Organizaciones", + "org.pendingmsg" : "Organización en espera de confirmación ", + "org.confirm" : "Confirmar", + "org.select" : "Seleccionar una organización", + "org.membersCount" : "Miembros", + "org.ohasdeleg" : "Los usuarios de esta organización pueden ser administrados por los siguientes usuarios:", + "org.userremoved" : "El usuario ha sido eliminado de la organización.", + "org.useradded" : "El usuario ha sido añadido a la organización.", + "org.userlabel" : "Añadir", + "org.description" : "Descripción", + "org.url" : "Website", + "org.logo" : "Logo", + "org.note" : "Interno Notas", + "org.cities" : "Zona", + "org.orgType" : "Organisation type", + "imageinput.label" : "Subir un logo", + "roles.title_plural" : "roles", + "roles.title_one" : "role", + "roles.title_none" : "Ningún role", + "roles.allroles" : "Todas", + "roles.newrole" : "Nuevo rol", + "roles.filter_roles" : "Filtrar", + "role.save" : "Guardar", + "role.updated" : "Rol modificada", + "role.deleted" : "Rol eliminada", + "role.created" : "Rol creada", + "role.error" : "Error al crear un rol", + "role.warning" : "Cuidado, esta acción no se puede deshacer — ", + "role.delete" : "Eliminar rol", + "role.rolelist" : "Todos los roles", + "role.confirm" : "Confirmar", + "role.select" : "Elija un rol", + "role.membersCount" : "Miembros", + "role.description" : "Descripción", + "role.cn" : "Nombre", + "role.favorite" : "Favorito", + "role.favorites" : "Favoritos", + "role.useradded" : "El usuario ha sido añadido al rol.", + "role.userremoved" : "El usuario ha sido eliminado del rol.", + "role.userlabel" : "Añadir", + "role.helpFormat" : "El identificador solo debe contener caracteres alfanuméricos en mayúsculas.", + "delegations.title" : "delegación de administración", + "delegations.title_plural" : "delegaciones de administración", + "delegations.title_no_delegations" : "Ninguna delegación de administración", + "delegations.filter" : "Filtrar", + "delegations.delete" : "Eliminar", + "delegation.dupdated" : "Delegación actualizada", + "delegation.ddeleted" : "Delegación eliminada", + "delegation.derror" : "Error al actualizar la delegación", + "delegation.restricted" : "No se permite añadir una delegación a un SUPERUSER.", + "area.save" : "Guardar la zona", + "area.updated" : "Zona de competencia actualizada", + "area.error" : "Error al guardar", + "area.selectByBBOX" : "Selección por BBOX", + "area.selection" : "Selección", + "area.area" : "Zona(s)", + "area.all" : "Todas", + "area.none" : "Ninguna", + "area.search" : "Buscar", + "area.import" : "Importar CSV", + "area.export" : "Exportar CSV", + "role.allusers" : "Todos los usuarios", + "role.system" : "Roles geOrchestra", + "role.app" : "Otros roles", + "role.newuser" : "Nuevo usuario", + "role.newrole" : "Nuevo rol", + "role.user" : "usuario", + "role.users" : "usuarios", + "role.users_none" : "Ningún usuario", + "role.filter_users" : "Filtrar", + "role.created" : "Rol creado", + "role.updated" : "Rol actualizado", + "role.deleted" : "Rol eliminado", + "role.error" : "Error al crear el rol", + "role.deleteError" : "Error al eliminar el rol", + "role.search" : "Buscar un rol", + "role.all" : "Todos", + "role.delete" : "Eliminar", + "users.createRole" : "Crear un rol", + "users.newRole" : "Nombre", + "users.newRoleName" : "Nombre del rol", + "users.newRoleDesc" : "Descripción", + "users.cancel" : "Cancelar", + "users.save" : "Guardar", + "users.delete" : "Eliminar", + "users.confirm" : "Confirmación", + "users.confirmText" : "¿Confirma la eliminación del rol?", + "users.roleUpdated" : "Rol actualizado", + "users.roleUpdateError" : "Error al actualizar el rol", + "users.user" : "Usuario", + "users.login" : "Identificador", + "users.organization" : "Organización", + "users.email" : "Correo electrónico", + "users.pending_desc" : "Usuarios pendientes de validación", + "users.filter_selected" : "Mostrar solo la selección", + "users.PENDING" : "Pendientes", + "users.ORGADMIN" : "Delegados", + "users.TEMPORARY" : "Interinos", + "users.EXPIRED" : "Caducado", + "users.SUPERUSER" : "Administradores de cuenta", + "users.ADMINISTRATOR" : "Administradores de datos", + "users.GN_ADMIN" : "Administradores de metadatos", + "users.GN_EDITOR" : "Editores de metadatos", + "users.GN_REVIEWER" : "Revisores de metadatos", + "users.EXTRACTORAPP" : "Extracción de datos", + "users.USER" : "Los usuarios", + "users.REFERENT" : "Referentes de su organización", + "sel.all" : "Todos", + "sel.none" : "Ningún", + "sel.export_csv" : "Exportar CSV", + "sel.export_vcard" : "Exportar VCard", + "msg.compose" : "Redactar un mensaje", + "msg.date" : "Fecha", + "msg.subject" : "Asunto", + "msg.nosubject" : "— sin asunto —", + "msg.sender" : "Remitente", + "msg.messages" : "Mensajes", + "msg.on" : "el", + "msg.attachments" : "Adjuntos", + "msg.templates" : "Elegir un modelo", + "msg.title" : "Título", + "msg.content" : "Contenido", + "msg.cancel" : "Cancelar", + "msg.send" : "Enviar", + "msg.sent" : "Mensaje enviado", + "msg.error" : "Error al enviar el mensaje", + "msg.empty" : "Sin mensajes", + "logs.title" : "Logs de administración", + "logs.logs" : "Logs de administración", + "logs.date" : "Fecha", + "logs.sender" : "Autor", + "logs.target" : "Destinatario", + "logs.type" : "Acción", + "logs.target" : "Para", + "logs.allsender" : "Todos", + "logs.alltype" : "Todos", + "logs.alltarget" : "Todos", + "logs.error" : "Error al cargar los logs", + "logs.target" : "Modificación de", + "logs.modification" : "Modificación del atributo", + "logs.replace" : "Reemplazo del valor", + "logs.to" : "por valor", + "logs.clear" : "Eliminar el valor", + "logs.set" : "Agregar valor", + "logs.all" : "Logs", + "logs.added" : "añaden elementos", + "logs.removed" : "añaden borrados ", + "logs.pendingusercreated" : "Usuario pendiente creada", + "logs.pendinguserrefused" : "Usuario pendiente rechazada", + "logs.pendinguseraccepted" : "Usuario pendiente aceptada", + "logs.pendingorgcreated" : "Organización pendiente creada", + "logs.pendingorgrefused" : "Organización pendiente rechazada", + "logs.pendingorgaccepted" : "Organización pendiente aceptada", + "logs.roledeleted" : "Rol eliminado", + "logs.orgcreated" : "Organización creada", + "logs.orgdeleted" : "Organización eliminada", + "logs.userpasswordchanged": "Contraseña cambiada", + "logs.usercreated" : "Usuario creado", + "logs.userdeleted" : "Usuario eliminado", + "logs.system" : "Rol del sistema", + "logs.custom" : "Rol personalizado", + "logs.roleadded" : "agregado al usuario", + "logs.roleremoved" : "eliminado del usuario", + "logs.emailrecoverysent": "Correo electrónico de recuperación de contraseña enviado", + "logs.rolecreated" : "Rol creado", + "analytics.title" : "Estadísticas", + "analytics.all" : "Todo el mundo", + "analytics.requests" : "Número de consultas", + "analytics.layers" : "Capas más consultadas", + "analytics.errorload" : "Error al cargar los datos", + "analytics.nodata" : "Sin datos para este periodo", + "analytics.graphview" : "Mostrar el gráfico", + "analytics.dataview" : "Mostrar la tabla", + "analytics.DAY" : "por día", + "analytics.WEEK" : "por semana", + "analytics.MONTH" : "por mes", + "analytics.HOUR" : "por hora", + "analytics.extractions" : "Extracciones de capas", + "analytics.saveAsPNG" : "Guardar en PNG", + "analytics.saveAsCSV" : "Guardar en CSV", + "date.day" : "Hoy", + "date.week" : "Última semana", + "date.month" : "Último mes", + "date.3month" : "Últimos tres meses", + "date.year" : "Último año", + "date.custom" : "Elegir un intervalo", + "date.to" : "a", + "sasl.remote.user" : "Usuario remoto en", + "sasl.tooltip" : "Este campo se utiliza para definir el nombre de usuario en el sistema remoto. Complete este campo para habilitar la autenticación de usuario remoto. Borre el campo para la autenticación local (se requerirá la regeneración de la contraseña).", + "editUserDetailsForm.deleteConfirm" : "¿Realmente quieres eliminar tu cuenta?", + "editUserDetailsForm.deleteFail" : "Error al eliminar cuenta" +} diff --git a/console/src/main/webapp/manager/app/assets/lang/fr.json b/console/src/main/webapp/manager/app/assets/lang/fr.json new file mode 100644 index 0000000000..c1e7052ccf --- /dev/null +++ b/console/src/main/webapp/manager/app/assets/lang/fr.json @@ -0,0 +1,290 @@ +{ + "nav.dashboard" : "Tableau de bord", + "nav.users" : "Utilisateurs", + "nav.orgs" : "Organismes", + "nav.analytics" : "Statistiques", + "nav.logs" : "Logs", + "nav.roles" : "Rôles", + "nav.delegations" : "Délégations", + "index.SUPERUSER" : "Administrateur", + "index.DELEGATED" : "Délégation", + "index.SUPERUSERtip" : "Votre rôle permet de modifier l'ensemble des comptes", + "index.DELEGATEDtip" : "Votre compte est habilité à gérer les utilisateurs d'une sélection d'organismes", + "home.users" : "Gérer les utilisateurs", + "home.waiting_users" : "utilisateurs en attente", + "home.waiting_user" : "utilisateurs en attente", + "home.expired_users" : "utilisateurs expirés aujourd’hui", + "home.expired_user" : "utilisateur expiré aujourd’hui", + "home.connected_users" : "utilisateurs des services aujourd’hui", + "home.connected_user" : "utilisateur des services aujourd’hui", + "home.connected_users_none" : "Aucun utilisateur nominatif des services aujourd’hui !", + "home.view_manage" : "Voir & gérer", + "home.nb_requests" : "Req.", + "home.user" : "Utilisateur", + "home.org" : "Org.", + "home.logs" : "Logs d’administration", + "tab.infos" : "Infos", + "tab.roles" : "Rôles", + "tab.delegations" : "Délégations", + "tab.analytics" : "Statistiques", + "tab.messages" : "Messages", + "tab.logs" : "Logs", + "tab.manage" : "Gérer", + "tab.area" : "Zone de compétence", + "tab.users" : "Membres", + "user.userlist" : "Tous les utilisateurs", + "user.updated" : "Utilisateur modifié", + "user.deleted" : "Utilisateur supprimé", + "user.created" : "Utilisateur créé", + "user.error" : "Erreur lors de la création de l’utilisateur", + "user.save" : "Enregistrer", + "user.delete" : "Supprimer l’utilisateur", + "user.new_user" : "Nouvel utilisateur", + "user.discard" : "Annuler", + "user.login" : "Login", + "user.cn" : "Nom complet", + "user.mail" : "Courriel", + "user.address" : "Adresse", + "user.postalAddress" : "Adresse", + "user.org" : "Organisme", + "user.description" : "Description", + "user.manager" : "Manager", + "user.expire" : "Date d'expiration", + "user.reset" : "Régénérer", + "user.password" : "Mot de passe local", + "user.sn" : "Nom", + "user.gn" : "Prénom", + "user.givenName" : "Prénom", + "user.phone" : "Téléphone", + "user.facsimileTelephoneNumber" : "Fax", + "user.fax" : "Fax", + "user.title" : "Fonction", + "user.privacyPolicyAgreementDate" : "Date d'acceptation des conditions", + "user.warning" : "Attention, cette action est irréversible — ", + "user.pendingmsg" : "Utilisateur en attente ", + "user.expiredmsg" : "Utilisateur expiré depuis le", + "user.confirm" : "Confirmer", + "user.orgFirst" : "Confirmer d’abord l’organisme : ", + "user.hasdeleg" : "Une délégation de gestion est active pour cet utilisateur", + "user.nodeleg" : "Pas de délégation de gestion définie pour cet utilisateur", + "user.manages_roles" : "Gère l'attribution des rôles :", + "user.manages_orgs" : "Parmi les utilisateurs appartenant aux organismes :", + "user.select_role" : "Sélectionner un rôle", + "user.action" : "Action", + "user.remove" : "Retirer", + "user.logs" : "Logs utilisateur", + "user.shadowExpire" : "Date d'expiration", + "user.preferredLanguage" : "Langue par défaut", + "user.telephoneNumber" : "Téléphone", + "user.uid" : "Identifiant", + "user.note" : "Notes internes", + "orgs.title_plural" : "organismes", + "orgs.title_one" : "organisme", + "orgs.title_none" : "Aucun organisme", + "orgs.allorgs" : "Tous les organismes", + "orgs.pending" : "A modérer", + "orgs.neworg" : "Nouvel organisme", + "orgs.filter_orgs" : "Filtrer", + "org.name" : "Nom", + "org.shortName" : "Nom court", + "org.type" : "Type d’organisme", + "org.address" : "Adresse", + "org.save" : "Enregistrer", + "org.updated" : "Organisme modifié", + "org.deleted" : "Organisme supprimé", + "org.created" : "Organisme créé", + "org.error" : "Erreur lors de la création de l’organisme", + "org.warning" : "Attention, cette action est irréversible — ", + "org.delete" : "Supprimer l’organisme", + "org.orglist" : "Organismes", + "org.pendingmsg" : "Organisme en attente ", + "org.confirm" : "Confirmer", + "org.select" : "Sélectionner un organisme", + "org.membersCount" : "Membres", + "org.ohasdeleg" : "Les utilisateurs de cet organisme peuvent être gérés par le(s) utilisateur(s) suivant(s):", + "org.userremoved" : "L'utilisateur a été retiré de l'organisme.", + "org.useradded" : "L'utilisateur a été ajouté à l'organisme.", + "org.userlabel" : "Ajouter", + "org.description" : "Description", + "org.url" : "Site web", + "org.logo" : "Logo", + "org.note" : "Notes Internes", + "org.cities" : "Aire de compétence", + "org.orgType" : "Type d'organisme", + "imageinput.label" : "Charger un logo", + "roles.title_plural" : "rôles", + "roles.title_one" : "rôle", + "roles.title_none" : "Aucun rôle", + "roles.allroles" : "Tous les rôles", + "roles.newrole" : "Nouveau rôle", + "roles.filter_roles" : "Filtrer", + "role.save" : "Enregistrer", + "role.updated" : "Rôle modifié", + "role.deleted" : "Rôle supprimé", + "role.created" : "Rôle créé", + "role.error" : "Erreur lors de la création du rôle", + "role.warning" : "Attention, cette action est irréversible — ", + "role.delete" : "Supprimer le rôle", + "role.rolelist" : "Tous les rôles", + "role.confirm" : "Confirmer", + "role.select" : "Sélectionner un rôle", + "role.membersCount" : "Membres", + "role.description" : "Description", + "role.cn" : "Identifiant", + "role.favorite" : "Favori", + "role.favorites" : "Favoris", + "role.useradded" : "L'utilisateur a été ajouté au rôle.", + "role.userremoved" : "L'utilisateur a été supprimé du rôle.", + "role.userlabel" : "Ajouter", + "role.helpFormat" : "L'identifiant ne doit contenir que des caractères alphanumériques en majuscules", + "delegations.title" : "délégation de gestion", + "delegations.title_plural" : "délégations de gestion", + "delegations.title_no_delegations" : "Aucune délégation de gestion", + "delegations.filter" : "Filtrer", + "delegations.delete" : "Supprimer", + "delegation.dupdated" : "Délégation mise à jour", + "delegation.ddeleted" : "Délégation supprimée", + "delegation.derror" : "Erreur lors de la mise à jour de la délégation", + "delegation.restricted" : "Impossible d’ajouter une délégation pour un SUPERUSER.", + "area.save" : "Enregistrer la zone", + "area.updated" : "Zone de compétence mise à jour", + "area.error" : "Erreur d’enregistrement", + "area.selectByBBOX" : "Sélection par BBOX", + "area.selection" : "Sélection", + "area.area" : "Zone(s)", + "area.all" : "Tout", + "area.none" : "Aucun", + "area.search" : "Rechercher", + "area.import" : "Import CSV", + "area.export" : "Export CSV", + "role.allusers" : "Tous les utilisateurs", + "role.system" : "Rôles geOrchestra", + "role.app" : "Autres rôles", + "role.newuser" : "Nouvel utilisateur", + "role.newrole" : "Nouveau rôle", + "role.user" : "utilisateur", + "role.users" : "utilisateurs", + "role.users_none" : "Aucun utilisateur", + "role.filter_users" : "Filtrer", + "role.created" : "Rôle créé", + "role.updated" : "Rôle mis à jour", + "role.deleted" : "Rôle supprimé", + "role.error" : "Erreur de création du rôle", + "role.deleteError" : "Erreur de suppression du rôle", + "role.search" : "Chercher un rôle", + "role.all" : "Tous", + "role.delete" : "Supprimer", + "users.createRole" : "Créer un rôle", + "users.newRole" : "Nom", + "users.newRoleName" : "Nom du rôle", + "users.newRoleDesc" : "Description", + "users.cancel" : "Annuler", + "users.save" : "Enregistrer", + "users.delete" : "Supprimer", + "users.confirm" : "Confirmation", + "users.confirmText" : "Souhaitez vous vraiment supprimer le rôle", + "users.roleUpdated" : "Rôle mis à jour", + "users.roleUpdateError" : "Erreur à l'association du rôle", + "users.user" : "Utilisateur", + "users.login" : "Login", + "users.organization" : "Organisme", + "users.email" : "Courriel", + "users.pending_desc" : "Utilisateurs en attente de validation", + "users.filter_selected" : "Afficher la sélection uniquement", + "users.PENDING" : "À modérer", + "users.ORGADMIN" : "Délégués", + "users.TEMPORARY" : "Temporaires", + "users.EXPIRED" : "Expirés", + "users.SUPERUSER" : "Administrateurs des comptes", + "users.ADMINISTRATOR" : "Administrateurs des données", + "users.GN_ADMIN" : "Administrateurs des métadonnées", + "users.GN_EDITOR" : "Éditeurs de métadonnées", + "users.GN_REVIEWER" : "Relecteurs de métadonnées", + "users.EXTRACTORAPP" : "Extraction des données", + "users.USER" : "Utilisateurs", + "users.REFERENT" : "Référents d'organisme", + "sel.all" : "Tous", + "sel.none" : "Aucun", + "sel.export_csv" : "Export CSV", + "sel.export_vcard" : "Export VCard", + "msg.compose" : "Composer un message", + "msg.date" : "Date", + "msg.subject" : "Sujet", + "msg.nosubject" : "— pas de sujet —", + "msg.sender" : "Expéditeur", + "msg.messages" : "Messages", + "msg.on" : "le", + "msg.attachments" : "Pièces jointes", + "msg.templates" : "Choisir un modèle", + "msg.title" : "Titre", + "msg.content" : "Contenu", + "msg.cancel" : "Annuler", + "msg.send" : "Envoyer", + "msg.sent" : "Message envoyé", + "msg.error" : "Erreur lors de l’envoi du message", + "msg.empty" : "Pas de messages", + "logs.title" : "Logs d’administration", + "logs.logs" : "Logs d’administration", + "logs.date" : "Date", + "logs.sender" : "Auteur", + "logs.target" : "Destinataire", + "logs.type" : "Action", + "logs.target" : "Cible", + "logs.allsender" : "Tous", + "logs.alltype" : "Tous", + "logs.alltarget" : "Tous", + "logs.error" : "Erreur au chargement des logs", + "logs.target" : "Modification de", + "logs.modification" : "Modification de l'attribut", + "logs.replace" : "Remplacement de la valeur", + "logs.to" : "par", + "logs.clear" : "Suppression de la valeur", + "logs.set" : "Ajout de la valeur", + "logs.all" : "Logs", + "logs.added" : "élément(s) ajouté(s) à l'emprise de compétence", + "logs.removed" : "élément(s) supprimé(s) de l'emprise de compétence", + "logs.pendingusercreated" : "Utilisateurs en attente de validation", + "logs.pendinguserrefused" : "Utilisateur en attente refusé", + "logs.pendinguseraccepted" : "Utilisateur en attente accepté", + "logs.pendingorgcreated" : "Organisme en attente de validation", + "logs.pendingorgrefused" : "Organisme en attente refusé", + "logs.pendingorgaccepted" : "Organisme en attente accepté", + "logs.roledeleted" : "Role supprimé", + "logs.orgcreated" : "Organisme créé", + "logs.orgdeleted" : "Organisme supprimé", + "logs.userpasswordchanged": "Modification du mot de passe", + "logs.usercreated" : "Utilisateur créé", + "logs.userdeleted" : "Utilisateur supprimé", + "logs.system" : "Le role système", + "logs.custom" : "Le role métier", + "logs.roleadded" : "a été ajouté à l'utilisateur", + "logs.roleremoved" : "a été enlevé de l'utilisateur", + "logs.emailrecoverysent": "Email de récupération du mot de passe envoyé", + "logs.rolecreated" : "Role créé", + "analytics.title" : "Statistiques", + "analytics.all" : "Tout le monde", + "analytics.requests" : "Nombre de requêtes", + "analytics.layers" : "Couches les + consultées", + "analytics.errorload" : "Erreur de chargement des données", + "analytics.nodata" : "Pas de données pour cette période", + "analytics.graphview" : "Vue en graphe", + "analytics.dataview" : "Vue en tableau", + "analytics.DAY" : "par jour", + "analytics.WEEK" : "par semaine", + "analytics.MONTH" : "par mois", + "analytics.HOUR" : "par heure", + "analytics.extractions" : "Extractions des couches", + "analytics.saveAsPNG" : "Enregistrer en PNG", + "analytics.saveAsCSV" : "Enregistrer en CSV", + "date.day" : "Aujourd’hui", + "date.week" : "Dernière semaine", + "date.month" : "Dernier mois", + "date.3month" : "Derniers 3 mois", + "date.year" : "Dernière année", + "date.custom" : "Choisir un intervalle", + "date.to" : "à", + "sasl.remote.user" : "Utilisateur distant sur", + "sasl.tooltip" : "Ce champ permet de définir le nom d'utilisateur sur le système distant. Remplir ce champ pour activer l'authentification distante de l'utilisateur. Vider le champ pour une authentification locale (une régénération du mot de passe sera nécessaire).", + "editUserDetailsForm.deleteConfirm" : "Êtes-vous sûr de vouloir supprimer votre compte ?", + "editUserDetailsForm.deleteFail" : "Échec de la suppression du compte" +} diff --git a/console/src/main/webapp/manager/app/assets/lang/nl.json b/console/src/main/webapp/manager/app/assets/lang/nl.json new file mode 100644 index 0000000000..19f510410a --- /dev/null +++ b/console/src/main/webapp/manager/app/assets/lang/nl.json @@ -0,0 +1,290 @@ +{ + "nav.dashboard" : "Dashboard", + "nav.users" : "Gebruikers", + "nav.orgs" : "Organisaties", + "nav.analytics" : "Statistieken", + "nav.logs" : "Logs", + "nav.roles" : "Rollen", + "nav.delegations" : "Delegaties", + "index.SUPERUSER" : "Beheerder", + "index.DELEGATED" : "Delegatie", + "index.SUPERUSERtip" : "Met uw rol kunt u alle accounts wijzigen", + "index.DELEGATEDtip" : "Uw account is geautoriseerd om de gebruikers van een selectie van organisaties te beheren", + "home.users" : "Gebruikers beheren", + "home.waiting_users" : "gebruikers in behandeling", + "home.waiting_user" : "gebruiker in behandeling", + "home.expired_users" : "gebruikers die vandaag verlopen zijn", + "home.expired_user" : "gebruiker die vandaag verlopen is", + "home.connected_users" : "dienst gebruikers vandaag", + "home.connected_user" : "dienst gebruiker vandaag", + "home.connected_users_none" : "Geen geregistreerde gebruiker van de services vandaag!", + "home.view_manage" : "Bekijken & beheren", + "home.nb_requests" : "Aantal queries", + "home.user" : "Gebruiker", + "home.org" : "Org.", + "home.logs" : "Beheerlogboeken", + "tab.infos" : "Infos", + "tab.roles" : "Rollen", + "tab.delegations" : "Delegaties", + "tab.analytics" : "Statistieken", + "tab.messages" : "Berichten", + "tab.logs" : "Logs", + "tab.manage" : "Beheren", + "tab.area" : "Gebied", + "tab.users" : "Leden", + "user.userlist" : "Alle gebruikers", + "user.updated" : "Gewijzigde gebruiker", + "user.deleted" : "Gebruiker verwijderd", + "user.created" : "Aangemaakte gebruiker", + "user.error" : "Fout bij aanmaken van gebruiker", + "user.save" : "Opslaan", + "user.delete" : "Gebruiker verwijderen", + "user.new_user" : "Nieuwe gebruiker", + "user.discard" : "Annuleren", + "user.login" : "Login", + "user.cn" : "Volledige naam", + "user.mail" : "Email", + "user.address" : "Adres", + "user.postalAddress" : "Adres", + "user.org" : "Organisatie", + "user.description" : "Beschrijving", + "user.manager" : "Manager", + "user.expire" : "Vervaldatum", + "user.reset" : "Regenereren", + "user.password" : "Wachtwoord", + "user.sn" : "Naam", + "user.gn" : "Voornaam", + "user.givenName" : "Voornaam", + "user.phone" : "Telefoon", + "user.facsimileTelephoneNumber" : "Fax", + "user.fax" : "Fax", + "user.title" : "Functie", + "user.privacyPolicyAgreementDate" : "Date of agreement to conditions", + "user.warning" : "Let op, deze actie is onomkeerbaar — ", + "user.pendingmsg" : "Gebruiker in behandeling ", + "user.expiredmsg" : "Expired user since", + "user.confirm" : "Bevestigen", + "user.orgFirst" : "Bevestig eerst organisatie : ", + "user.hasdeleg" : "Een delegatie van beheer is actief voor deze gebruiker", + "user.nodeleg" : "Geen managementdelegatie gedefinieerd voor deze gebruiker", + "user.manages_roles" : "Beheert de toewijzing van rollen :", + "user.manages_orgs" : "Onder de gebruikers die tot de organisaties behoren :", + "user.select_role" : "Selecteer een rol", + "user.action" : "Actie", + "user.remove" : "Verwijderen", + "user.logs" : "User logs", + "user.shadowExpire" : "Expiration date", + "user.preferredLanguage": "Default language", + "user.telephoneNumber" : "Phone number", + "user.uid" : "Identifier", + "user.note" : "Internal notes", + "orgs.title_plural" : "organisaties", + "orgs.title_one" : "organisatie", + "orgs.title_none" : "Geen organisatie", + "orgs.allorgs" : "Alle organisaties", + "orgs.pending" : "In behandeling", + "orgs.neworg" : "Nieuwe organisatie", + "orgs.filter_orgs" : "Filter", + "org.name" : "Naam", + "org.shortName" : "Korte naam", + "org.type" : "Type organisatie", + "org.address" : "Adres", + "org.save" : "Opslaan", + "org.updated" : "Gewijzigde organisatie", + "org.deleted" : "Organisatie verwijderd", + "org.created" : "Aangemaakt organisatie", + "org.error" : "Fout bij het maken van de organisatie", + "org.warning" : "Waarschuwing, deze actie is onomkeerbaar — ", + "org.delete" : "Organisatie verwijderen", + "org.orglist" : "Organisaties", + "org.pendingmsg" : "Organisatie in behandeling ", + "org.confirm" : "Bevestigen", + "org.select" : "Selecteer een organisatie", + "org.membersCount" : "Leden", + "org.ohasdeleg" : "De gebruikers van deze organisatie kunnen worden beheerd door de volgende gebruiker(s):", + "org.userremoved" : "De gebruiker is verwijderd uit de organisatie.", + "org.useradded" : "De gebruiker is toegevoegd aan de organisatie.", + "org.userlabel" : "Toevoegen", + "org.description" : "Description", + "org.url" : "Website", + "org.logo" : "Logo", + "org.note" : "Internal Notes", + "org.cities" : "Area", + "org.orgType" : "Organisation type", + "imageinput.label" : "Upload logo", + "roles.title_plural" : "rollen", + "roles.title_one" : "rol", + "roles.title_none" : "Geen rol", + "roles.allroles" : "Alle rollen", + "roles.newrole" : "Nieuwe rol", + "roles.filter_roles" : "Filter", + "role.save" : "Opslaan", + "role.updated" : "Gewijzigde rol", + "role.deleted" : "Rol verwijderd", + "role.created" : "Rol aangemaakt", + "role.error" : "Fout bij het maken van de rol", + "role.warning" : "Waarschuwing, deze actie is onomkeerbaar — ", + "role.delete" : "De rol verwijderen", + "role.rolelist" : "Alle rollen", + "role.confirm" : "Bevestigen", + "role.select" : "Selecteer een rol", + "role.membersCount" : "Leden", + "role.description" : "Beschrijving", + "role.cn" : "Identifier", + "role.favorite" : "Favoriet", + "role.favorites" : "Favorieten", + "role.useradded" : "De gebruiker is toegevoegd aan de rol.", + "role.userremoved" : "De gebruiker is verwijderd uit de rol.", + "role.userlabel" : "Toevoegen", + "role.helpFormat" : "De identificatie mag alleen hoofdletters en alfanumerieke tekens bevatten.", + "delegations.title" : "beheer delegatie", + "delegations.title_plural" : "beheer delegaties", + "delegations.title_no_delegations" : "geen beheer delegatie", + "delegations.filter" : "Filter", + "delegations.delete" : "Verwijderen", + "delegation.dupdated" : "Delegatie bijgewerkt", + "delegation.ddeleted" : "Delegatie verwijderd", + "delegation.derror" : "Fout bij bijwerken van de delegatie", + "delegation.restricted" : "Kan geen delegatie toevoegen voor een SUPERUSER.", + "area.save" : "Gebied opslaan", + "area.updated" : "Bijgewerkt gebied", + "area.error" : "Fout met opslaan", + "area.selectByBBOX" : "Selectie met BBOX", + "area.selection" : "Selectie", + "area.area" : "Gebied(en)", + "area.all" : "Alles", + "area.none" : "Geen", + "area.search" : "Zoeken", + "area.import" : "CSV importeren", + "area.export" : "CSV exporteren", + "role.allusers" : "Alle gebruikers", + "role.system" : "geOrchestra Rollen", + "role.app" : "Andere rollen", + "role.newuser" : "Nieuwe gebruiker", + "role.newrole" : "Nieuwe rol", + "role.user" : "gebruiker", + "role.users" : "gebruikers", + "role.users_none" : "Geen gebruiker", + "role.filter_users" : "Filter", + "role.created" : "Rol aangemaakt", + "role.updated" : "Rol bijgewerkt", + "role.deleted" : "Rol verwijderd", + "role.error" : "Fout bij het aanmaken van de rol", + "role.deleteError" : "Fout bij het verwijderen van de rol", + "role.search" : "Zoeken naar een rol", + "role.all" : "Alles", + "role.delete" : "Verwijderen", + "users.createRole" : "Een rol aanmaken", + "users.newRole" : "Naam", + "users.newRoleName" : "Naamm va de rol", + "users.newRoleDesc" : "Beschrijving", + "users.cancel" : "Annuleren", + "users.save" : "Opslaan", + "users.delete" : "Verwijderen", + "users.confirm" : "Bevestiging", + "users.confirmText" : "Weet u zeker dat u de rol wilt verwijderen", + "users.roleUpdated" : "Rol bijgewerkt", + "users.roleUpdateError" : "Fout bij koppelen van de rol", + "users.user" : "Gebruiker", + "users.login" : "Login", + "users.organization" : "Organisatie", + "users.email" : "Email", + "users.pending_desc" : "Gebruikers wachten op validatie", + "users.filter_selected" : "Afficher la sélection uniquement", + "users.PENDING" : "In behandeling", + "users.ORGADMIN" : "Delegates", + "users.TEMPORARY" : "Temporary", + "users.EXPIRED" : "Expired", + "users.SUPERUSER" : "Accounts administrators", + "users.ADMINISTRATOR" : "Data administrators", + "users.GN_ADMIN" : "Metadata administrators", + "users.GN_EDITOR" : "Metadata editors", + "users.GN_REVIEWER" : "Metadata reviewers", + "users.EXTRACTORAPP" : "Data extractor", + "users.USER" : "Users", + "users.REFERENT" : "Organization referent", + "sel.all" : "All", + "sel.none" : "None", + "sel.export_csv" : "CSV Export", + "sel.export_vcard" : "VCard Export", + "msg.compose" : "Een bericht opstellen", + "msg.date" : "Datum", + "msg.subject" : "Onderwerp", + "msg.nosubject" : "— geen onderwerp —", + "msg.sender" : "Afzender", + "msg.messages" : "Berichten", + "msg.on" : "op", + "msg.attachments" : "Bijlagen", + "msg.templates" : "Kies een model", + "msg.title" : "Titel", + "msg.content" : "Inhoud", + "msg.cancel" : "Annuleren", + "msg.send" : "Verzenden", + "msg.sent" : "Bericht verzonden", + "msg.error" : "Fout bij verzenden van bericht", + "msg.empty" : "Geen bericht", + "logs.title" : "Beheerlogboeken", + "logs.logs" : "Beheerlogboeken", + "logs.date" : "Datum", + "logs.sender" : "Beheer", + "logs.target" : "Ontvanger", + "logs.type" : "Actie", + "logs.target" : "Doelwit", + "logs.allsender" : "Alles", + "logs.alltype" : "Alles", + "logs.alltarget" : "Alles", + "logs.error" : "Fout bij laden van logboeken", + "logs.target" : "Modification of", + "logs.modification" : "Modification on the attribute", + "logs.replace" : "Replace the value", + "logs.to" : "by", + "logs.clear" : "Clear the value", + "logs.set" : "Set the value", + "logs.all" : "Logs", + "logs.added" : "Item(s) added", + "logs.removed" : "item(s) removed ", + "logs.pendingusercreated" : "Pending user created", + "logs.pendinguserrefused" : "Pending user refused", + "logs.pendinguseraccepted" : "Pending user accepted", + "logs.pendingorgcreated" : "Pending org created", + "logs.pendingorgrefused" : "Pending org refused", + "logs.pendingorgaccepted" : "Pending org accepted", + "logs.roledeleted" : "Role deleted", + "logs.orgcreated" : "Organisation created", + "logs.orgdeleted" : "Organisation deleted", + "logs.userpasswordchanged": "Password changed", + "logs.usercreated" : "User created", + "logs.userdeleted" : "User deleted", + "logs.system" : "System role", + "logs.custom" : "Custom role", + "logs.roleadded" : "added to user", + "logs.roleremoved" : "removed from this user", + "logs.emailrecoverysent": "Password recovery email sent", + "logs.rolecreated" : "Role created", + "analytics.title" : "Statistieken", + "analytics.all" : "Iedereen", + "analytics.requests" : "Aantal queries", + "analytics.layers" : "Meest bekeken lagen", + "analytics.errorload" : "Fout bij laden van gegevens", + "analytics.nodata" : "Geen gegevens voor deze periode", + "analytics.graphview" : "Grafiekweergave", + "analytics.dataview" : "Tabelweergave", + "analytics.DAY" : "per dag", + "analytics.WEEK" : "per week", + "analytics.MONTH" : "per maand", + "analytics.HOUR" : "per uur", + "analytics.extractions" : "Laaguittreksels", + "analytics.saveAsPNG" : "Opslaan als PNG", + "analytics.saveAsCSV" : "Opslaan als CSV", + "date.day" : "Vandaag", + "date.week" : "Vorige week", + "date.month" : "Vorige maand", + "date.3month" : "3 laatste maanden", + "date.year" : "Vorig jaar", + "date.custom" : "Kies een interval", + "date.to" : "tot", + "sasl.remote.user" : "Externe gebruiker op", + "sasl.tooltip" : "Dit veld wordt gebruikt om de gebruikersnaam op het externe systeem te definiëren. Vul dit veld in om gebruikersverificatie op afstand in te schakelen. Maak het veld leeg voor lokale authenticatie (opnieuw genereren van wachtwoord is vereist).", + "editUserDetailsForm.deleteConfirm" : "Wil je je account echt verwijderen?", + "editUserDetailsForm.deleteFail" : "Account verwijderen is mislukt" +} diff --git a/console/src/main/webapp/manager/app/components/analytics/analytics.es6 b/console/src/main/webapp/manager/app/components/analytics/analytics.es6 new file mode 100644 index 0000000000..f9735e6a8b --- /dev/null +++ b/console/src/main/webapp/manager/app/components/analytics/analytics.es6 @@ -0,0 +1,90 @@ +require('components/analytics/analytics.tpl') +require('components/date/date') + +require('services/analytics') + +class AnalyticsController { + constructor ($injector, $routeParams) { + this.$injector = $injector + this.i18n = {} + this.$injector.get('translate')('analytics.all', this.i18n) + + this.role = $routeParams.role || 'all' + this.roles = this.$injector.get('Role').query(() => { + this.roles = [{ cn: 'all' }].concat(this.roles).map(g => { + g.label = this.i18n[g.cn] || g.cn + return g + }) + }) + const date = this.$injector.get('date') + + this.date = { + start: date.getDefault(), + end: date.getEnd() + } + + this.data = {} + this.config = { + layers: ['layer', 'count'], + requests: ['date', 'count'], + extractions: ['layer', 'count'] + } + + this.load((this.role !== 'all') ? this.role : undefined) + } + + load (role) { + const i18n = {} + this.$injector.get('translate')('analytics.errorload', i18n) + this.$injector.get('translate')('users.roleUpdateError', i18n) + const Flash = this.$injector.get('Flash') + const Analytics = this.$injector.get('Analytics') + const err = Flash.create.bind(Flash, 'danger', i18n.errorload) + + const options = { + service: 'combinedRequests.json', + startDate: this.date.start, + endDate: this.date.end + } + if (role && role !== 'all') { + options.role = role + } + + this.requests = Analytics.get(options, () => {}, err) + + this.requestsOptions = { ...options } + this.requestsOptions.service = 'combinedRequests.csv' + + const usageOptions = { + ...options, + service: 'layersUsage.json', + limit: 10 + } + + this.layers = Analytics.get(usageOptions, () => {}, err) + + this.usageOptions = { ...usageOptions } + delete this.usageOptions.limit + this.usageOptions.service = 'layersUsage.csv' + + const extractionOptions = { + ...options, + service: 'layersExtraction.json', + limit: 10 + } + + this.extractions = Analytics.get(extractionOptions, () => {}, err) + this.extractionOptions = { ...extractionOptions } + delete this.extractionOptions.limit + this.extractionOptions.service = 'layersExtraction.csv' + } + + setRole () { + const $router = this.$injector.get('$router') + $router.navigate($router.generate('analytics', { role: this.role })) + } +} + +AnalyticsController.$inject = ['$injector', '$routeParams', 'Analytics'] + +angular.module('manager').controller('AnalyticsController', AnalyticsController) diff --git a/console/src/main/webapp/manager/app/components/analytics/analytics.tpl.html b/console/src/main/webapp/manager/app/components/analytics/analytics.tpl.html new file mode 100644 index 0000000000..ac79f19c19 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/analytics/analytics.tpl.html @@ -0,0 +1,43 @@ +
+ + + + + +

+ analytics.title + + for + {{analytics.role}} + +

+ +
+ +
+ + +
+
+
+ +
+ +
diff --git a/console/src/main/webapp/manager/app/components/area/area.es6 b/console/src/main/webapp/manager/app/components/area/area.es6 new file mode 100644 index 0000000000..eadc315f11 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/area/area.es6 @@ -0,0 +1,286 @@ +require('components/area/area.tpl') + +const buildStyle = (fillColor, strokeColor, width) => new ol.style.Style({ + fill: new ol.style.Fill({ color: fillColor }), + stroke: new ol.style.Stroke({ color: strokeColor, width: width || 1 }) +}) +const highlightStyle = buildStyle([255, 0, 0, 0.5], [255, 0, 0, 0.2]) +const highlight = feature => { + feature.setStyle(highlightStyle) + setTimeout(() => feature.setStyle(), 250) + return feature +} + +class AreaController { + static $inject = ['$injector', '$http', '$scope'] + + constructor ($injector, $http, $scope) { + this.$injector = $injector + this.$scope = $scope + + const translate = $injector.get('translate') + this.i18n = {} + translate('area.updated', this.i18n) + translate('area.error', this.i18n) + this.canExport = window.Blob && window.FileReader + } + + $onInit () { + this.maponly = this.readonly === 'true' + const $http = this.$injector.get('$http') + const CONFIG_URI = this.$injector.get('CONSOLE_PUBLIC_PATH') + 'orgs/areaConfig.json' + const promises = [$http.get(CONFIG_URI).then(r => r.data)] + this.canExport = this.canExport && this.item.id + if (this.item.$promise) { + promises.push(this.item.$promise) + } + this.$injector.get('$q').all(promises).then(this.initialize.bind(this)) + } + + initialize (resps) { + const [config] = resps + this.ids = this.item.cities || [] + this.groups = [] + + this.collection = new ol.Collection() + + const vector = new ol.layer.Vector({ + source: new ol.source.Vector(), + style: f => this.collection.getArray().indexOf(f) >= 0 + ? buildStyle([0, 159, 227, 0.2], [0, 159, 227, 1], 1.5) + : buildStyle([255, 255, 255, 0.1], [0, 0, 0, 0.2]) + + }) + this.source = vector.getSource() + + const map = new ol.Map({ + target: document.querySelector('.map'), + layers: [ + new ol.layer.Tile({ + source: new ol.source.OSM({ attributions: null }) + }), + vector + ], + logo: false + }) + this.map = map + this.vector = vector + + if (!this.maponly) { + map.on( + 'click', + e => map.forEachFeatureAtPixel(e.pixel, f => { + if (this.collection.getArray().indexOf(f) >= 0) { + this.collection.remove(f) + this.updateSelection([], true) + } else { + this.updateSelection([f], true) + } + }) + ) + } + + const format = new ol.format.GeoJSON() + + this.loading = true + this.$injector.get('$http').get('/console/public/area.geojson').then( + response => response.data + ).then(json => { + const conf = { + dataProjection: 'EPSG:4326', + featureProjection: map.getView().getProjection() + } + const selected = [] + json.features.forEach(f => { + f.id = f.properties[config.areas.key].toString() + }) + vector.getSource().addFeatures(format.readFeatures(json, conf)) + vector.getSource().forEachFeature(f => { + const group = f.get(config.areas.group) + if (group === undefined) { + throw new Error(`Cannot get AreaGroup "${config.areas.group}" in provided geojson. Check datadir config.`) + } + if (this.groups.indexOf(group) < 0) { + this.groups.push(group) + } + const displayName = f.get(config.areas.value) + if (displayName === undefined) { + throw new Error(`Cannot get AreaValue "${config.areas.value}" in provided geojson. Check datadir config.`) + } + f.set('_label', displayName.toString() || '') + f.set('_group', group) + if (this.ids.indexOf(f.getId()) >= 0) selected.push(f) + }) + + this.map.set('config', config.map) + if (!config.map) { + this.map.getView().fit(vector.getSource().getExtent(), map.getSize()) + } else { + this.map.getView().setCenter(ol.proj.fromLonLat(config.map.center)) + this.map.getView().setZoom(config.map.zoom) + } + + if (selected.length > 0) { + const extent = ol.extent.createEmpty() + selected.forEach(f => ol.extent.extend(extent, f.getGeometry().getExtent())) + this.map.getView().fit(extent, map.getSize()) + } + + this.updateSelection(selected) + this.loading = false + }).catch(ex => console.error(ex)) + + const dragBox = new ol.interaction.DragBox({ + condition: ol.events.condition.always + }) + map.getInteractions().push(dragBox) + dragBox.setActive(this.draw = false) + dragBox.on('boxend', () => { + const selected = [] + vector.getSource().forEachFeatureIntersectingExtent( + dragBox.getGeometry().getExtent(), + (feature) => { selected.push(feature) } + ) + this.updateSelection(selected, true) + dragBox.setActive(this.draw = false) + }) + + const buildRE = (search) => { + search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // eslint-disable-line + return new RegExp('(' + search.split(' ').join('|') + ')', 'gi') + } + + new autoComplete({ // eslint-disable-line + selector: document.querySelector('.search'), + minChars: 3, + source: (term, response) => { + const matches = [] + const re = buildRE(term) + vector.getSource().getFeatures().forEach(f => { + if (f.get('_label').match(re)) { matches.push(f) } + }) + response(matches) + }, + renderItem: (f, search) => + '
' + + '' + + ((this.collection.getArray().indexOf(f) >= 0) ? '✓' : '') + + '' + + f.get('_label').replace(buildRE(search), '$1') + + '
', + onSelect: (e, term, item) => { + const f = vector.getSource().getFeatureById(item.getAttribute('data-id')) + if (this.collection.getArray().indexOf(f) >= 0) { + this.collection.remove(f) + this.updateSelection([], true) + } else { + this.updateSelection([f], true) + } + f.setStyle(buildStyle([255, 0, 0, 0.5], [255, 0, 0, 0.2])) + setTimeout(() => f.setStyle(), 350) + } + }) + + this.selectBBOX = () => { + dragBox.setActive(this.draw = true) + } + + this.selectBy = () => { + if (this.group === 'all') { + this.updateSelection(vector.getSource().getFeatures()) + return + } + if (this.group === 'none') { + this.updateSelection([]) + this.group = '' + return + } + + const selected = vector.getSource().getFeatures().filter( + f => f.get('_group') === this.group + ) + this.updateSelection(selected) + } + } + + updateSelection (features, cumulative = false) { + this.$injector.get('$timeout')(() => { + if (!cumulative) this.collection.clear() + const uniques = this.collection.getArray().concat(features).filter( + (item, index, self) => index === self.indexOf(item) + ) + this.collection.clear() + this.collection.extend(uniques) + this.ids = uniques.map(f => f.getId()) + features.map(highlight) + this.collection.getArray().sort( + (a, b) => a.get('_label').localeCompare(b.get('_label')) + ) + this.vector.changed() + }) + } + + removeFromSelection (feature) { + highlight(feature) + this.collection.remove(feature) + this.ids = this.collection.getArray().map(f => f.getId()) + } + + save () { + const flash = this.$injector.get('Flash') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + + this.item.cities = this.ids + this.item.$update(() => { + $httpDefaultCache.removeAll() + flash.create('success', this.i18n.updated) + }, flash.create.bind(flash, 'danger', this.i18n.error)) + } + + export () { + const a = document.createElement('a') + a.href = window.URL.createObjectURL(new Blob( + [this.ids.join('\n')], + { type: 'text/csv' } + )) + a.download = 'export.csv' + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + } + + import () { + const fileInput = document.createElement('input') + const reader = new window.FileReader() + fileInput.type = 'file' + fileInput.accept = 'text/csv' + this.ids = [] + this.collection.clear() + reader.onload = () => this.$scope.$apply(() => { + reader.result.replace(/\r/g, '').split('\n').forEach(line => { + const [id] = line.split(/,|;/) + const f = this.source.getFeatureById(id) + if (!f) return + this.collection.push(highlight(f)) + this.ids.push(id) + }) + }) + fileInput.addEventListener( + 'change', + () => reader.readAsText(fileInput.files[0])) + fileInput.click() + } +} + +angular.module('manager').component('areas', { + bindings: { + readonly: '=', + item: '=', + callback: '=' + }, + controller: AreaController, + controllerAs: 'area', + templateUrl: 'components/area/area.tpl.html' +}) diff --git a/console/src/main/webapp/manager/app/components/area/area.tpl.html b/console/src/main/webapp/manager/app/components/area/area.tpl.html new file mode 100644 index 0000000000..60b5e273c8 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/area/area.tpl.html @@ -0,0 +1,62 @@ +
+ +
+
+
+ © OpenStreetMap + contributors. +
+
+ + +
+ +
+ + +
+
+
+ +
+ +

+ {{area.collection.getArray().length}} {{ 'area.area' | translate }} + + +

+ +
    +
  • + + + + {{f.get('_label')}} +
  • +
+ + +
+ + +
+ +
+ +
+ +
+ +
diff --git a/console/src/main/webapp/manager/app/components/browse/browse.es6 b/console/src/main/webapp/manager/app/components/browse/browse.es6 new file mode 100644 index 0000000000..3b962f12c9 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/browse/browse.es6 @@ -0,0 +1,81 @@ +import 'components/browse/browse.tpl' +import 'services/roles' + +class BrowseController { + static $inject = ['$injector'] + + constructor ($injector) { + this.$injector = $injector + this.pendingCount = 0 + const PROTECTED = this.$injector.get('readonlyRoleList') + this.filterRole = (protecteds, role) => protecteds ^ !PROTECTED.includes(role) + + // Rebind method for outer use + this.protected = this.protected.bind(this) + this.unprotected = this.unprotected.bind(this) + } + + $onInit () { + const roleAdminList = this.$injector.get('roleAdminList') + if (this.roles.$promise) { + this.$injector.get('$q').all([ + this.roles.$promise, + this.activePromise + ]).then(this.initialize.bind(this, roleAdminList)) + } else { + this.initialize(roleAdminList) + } + } + + initialize (roleAdminList) { + this.activeRole = this.activePromise.$$state.value + + const index = {} + this.q = (this.q) || '' + + this.roles.forEach(role => { index[role.cn] = role }) + this.index = index + + const fullAdminList = roleAdminList() + this.adminList = [] + for (const idx in this.index) { + const role = this.index[idx] + if (fullAdminList.indexOf(role.cn) >= 0) { + this.adminList.push(role) + } + } + this.favoriteRole = this.favoriteRole.bind(this) + this.$injector.get('User').query(users => ( + this.pendingCount = users.filter(u => u.pending).length + )) + } + + favoriteRole (role) { + return role.isFavorite && this.adminList.indexOf(role) === -1 + } + + createRole () { + const $location = this.$injector.get('$location') + $location.search('new', 'role') + } + + protected (role) { + return this.filterRole(true, role.cn) + } + + unprotected (role) { + return this.filterRole(false, role.cn) + } +} + +angular.module('manager') + .component('browse', { + bindings: { + roles: '=', + activePromise: '=', + index: '=?' + }, + controller: BrowseController, + controllerAs: 'roles', + templateUrl: 'components/browse/browse.tpl.html' + }) diff --git a/console/src/main/webapp/manager/app/components/browse/browse.tpl.html b/console/src/main/webapp/manager/app/components/browse/browse.tpl.html new file mode 100644 index 0000000000..8f93e6efec --- /dev/null +++ b/console/src/main/webapp/manager/app/components/browse/browse.tpl.html @@ -0,0 +1,48 @@ + + + + +
role.system
+
+ + {{::role.cn}} ({{::role.users.length}}) + + + {{::role.cn}} + ({{::role.users.length}}) + +
+ +
role.favorites
+
+ + {{::role.cn}} ({{::role.users.length}}) + + + {{::role.cn}} + ({{::role.users.length}}) + +
+ + diff --git a/console/src/main/webapp/manager/app/components/date/date.es6 b/console/src/main/webapp/manager/app/components/date/date.es6 new file mode 100644 index 0000000000..290b22825c --- /dev/null +++ b/console/src/main/webapp/manager/app/components/date/date.es6 @@ -0,0 +1,54 @@ +require('components/date/date.tpl') + +class DateController { + static $inject = ['$injector', '$scope', '$element'] + + constructor ($injector, $scope, $element) { + this.date = $injector.get('date') + + this.options = ['day', 'week', 'month', '3month', 'year', 'custom'].map( + x => { return { value: x, label: 'date.' + x } } + ) + this.option = this.options[this.options.length - 2] + + $scope.$watch('date.model.start', (newVal, oldVal) => { + if (!newVal || this.option.value === 'custom') { return } + this.option = this.options.filter( + x => this.date.getFromDiff(x.value) === newVal + )[0] + }) + + // Reload on custom date changes + const dateChanged = (val, old) => { + if (!this.option) { return } + if (this.option.value === 'custom' && val !== old) { this.callback() } + } + $scope.$watch('date.model.start', dateChanged) + $scope.$watch('date.model.end', dateChanged) + + $element.find('.input-daterange').datepicker({ format: 'yyyy-mm-dd' }) + } + + change () { + if (this.option.value !== 'custom') { + this.model.start = this.date.getFromDiff(this.option.value) + } + this.callback() + } +} + +angular.module('manager').component('date', { + bindings: { + model: '=', + callback: '&' + }, + controller: DateController, + controllerAs: 'date', + templateUrl: 'components/date/date.tpl.html' +}) + .directive('datepicker', () => ({ + require: 'ngModel', + link: (scope, elm, attrs, ctrl) => { + elm.datepicker({ format: 'yyyy-mm-dd' }) + } + })) diff --git a/console/src/main/webapp/manager/app/components/date/date.tpl.html b/console/src/main/webapp/manager/app/components/date/date.tpl.html new file mode 100644 index 0000000000..07fd99fbf2 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/date/date.tpl.html @@ -0,0 +1,9 @@ +
+ + {{ 'date.to' | translate }} + +
+ diff --git a/console/src/main/webapp/manager/app/components/delegations/delegations.es6 b/console/src/main/webapp/manager/app/components/delegations/delegations.es6 new file mode 100644 index 0000000000..bb096bb872 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/delegations/delegations.es6 @@ -0,0 +1,18 @@ +import 'components/delegations/delegations.tpl' +import 'services/delegations' + +class DelegationsController { + static $inject = ['Delegations', 'Orgs'] + + constructor (Delegations, Orgs) { + this.delegations = Delegations.query() + this.orgs = {} + Orgs.query(orgs => orgs.forEach(org => (this.orgs[org.id] = org))) + this.q = '' + this.itemsPerPage = 15 + } +} + +angular + .module('manager') + .controller('DelegationsController', DelegationsController) diff --git a/console/src/main/webapp/manager/app/components/delegations/delegations.tpl.html b/console/src/main/webapp/manager/app/components/delegations/delegations.tpl.html new file mode 100644 index 0000000000..762fa70e19 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/delegations/delegations.tpl.html @@ -0,0 +1,49 @@ +
+ +
+ + + +

+ + +

+ +
+ + + + + + + + + + + + + + + + + +
home.usernav.orgsnav.roles
+ {{::delegation.uid}} + + + {{ delegations.orgs[org].name }}{{$last ? '' : ', '}} + + + + {{ role }}{{$last ? '' : ', '}} + +
+ + + + + + diff --git a/console/src/main/webapp/manager/app/components/home/home.es6 b/console/src/main/webapp/manager/app/components/home/home.es6 new file mode 100644 index 0000000000..6007df24b3 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/home/home.es6 @@ -0,0 +1,44 @@ +require('components/home/home.tpl') + +class HomeController { + static $inject = ['$injector'] + + constructor ($injector) { + const EXPIRED_ROLE = $injector.get('expiredRole') + + this.$injector = $injector + + $injector.get('Role').query(roles => { + this.expired = roles.find(r => (r.cn === EXPIRED_ROLE)) + }) + this.pendingCount = 0 + $injector.get('User').query(users => { + this.pendingCount = users.filter(u => u.pending).length + }) + + this.i18n = {} + $injector.get('translate')('analytics.errorload', this.i18n) + + const flash = $injector.get('Flash') + + const Analytics = $injector.get('Analytics') + const options = { + service: 'distinctUsers', + startDate: $injector.get('date').getFromDiff('day'), + endDate: $injector.get('date').getEnd() + } + + this.connected = Analytics.get(options, () => {}, () => { + flash.create('danger', this.i18n.errorload) + }) + this.requests = Analytics.get({ + ...options, + service: 'combinedRequests.json', + startDate: $injector.get('date').getFromDiff('week') + }, () => {}, () => { + flash.create('danger', this.i18n.errorload) + }) + } +} + +angular.module('manager').controller('HomeController', HomeController) diff --git a/console/src/main/webapp/manager/app/components/home/home.tpl.html b/console/src/main/webapp/manager/app/components/home/home.tpl.html new file mode 100644 index 0000000000..b5292707ff --- /dev/null +++ b/console/src/main/webapp/manager/app/components/home/home.tpl.html @@ -0,0 +1,66 @@ +
+ +
+ + +
+

{{::home.connected.results.length}}

+

+

+
+ + + + + + + + + + + + + + + +
home.nb_requestshome.userhome.org
{{::user.nb_requests}}{{::user.user}}{{::user.organization}}
+ +
+
+ +
+ +
+ + + +
+ +
diff --git a/console/src/main/webapp/manager/app/components/imageinput/imageinput.es6 b/console/src/main/webapp/manager/app/components/imageinput/imageinput.es6 new file mode 100644 index 0000000000..6fa15601a6 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/imageinput/imageinput.es6 @@ -0,0 +1,48 @@ +require('components/imageinput/imageinput.tpl') + +class ImageinputController { + static $inject = ['$element', '$scope'] + constructor ($element, $scope) { + this.$element = $element + this.$scope = $scope + } + + $onInit () { + if (!window.FileReader) return + const reader = new window.FileReader() + const fileinput = this.$element.find('.image-input') + + fileinput.on('change', () => { + const file = fileinput[0].files[0] + reader.addEventListener('load', () => + this.$scope.$apply(() => this.setValue(reader.result.split(',')[1])) + ) + file && reader.readAsDataURL(file) + }) + // Initial value + const setValue = () => (this.value = this.model ? this.model[this.attribute] : null) + if (this.model && this.model.$promise) this.model.$promise.then(setValue) + else setValue() + } + + delete () { + this.setValue('') + } + + setValue (value) { + this.value = value + if (this.model) this.model[this.attribute] = value + if (this.target) document.querySelector(this.target).value = value + } +} + +angular.module('manager').component('imageinput', { + bindings: { + model: '=', + attribute: '=', + target: '=' + }, + controller: ImageinputController, + controllerAs: 'imageinput', + templateUrl: 'components/imageinput/imageinput.tpl.html' +}) diff --git a/console/src/main/webapp/manager/app/components/imageinput/imageinput.tpl.html b/console/src/main/webapp/manager/app/components/imageinput/imageinput.tpl.html new file mode 100644 index 0000000000..4e2b4dc182 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/imageinput/imageinput.tpl.html @@ -0,0 +1,22 @@ +
+
+ +
+
+ + +
+
diff --git a/console/src/main/webapp/manager/app/components/logger/logger.es6 b/console/src/main/webapp/manager/app/components/logger/logger.es6 new file mode 100644 index 0000000000..a78361e371 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/logger/logger.es6 @@ -0,0 +1,252 @@ +require('components/logger/logger.tpl') + +class LoggerController { + static $inject = ['$element', '$scope', '$injector'] + constructor ($element, $scope, $injector) { + this.$injector = $injector + this.$element = $element + this.$scope = $scope + } + + $onInit () { + this.itemsPerPage = 15 + this.i18n = {} + ;[ + 'error', + 'alltarget', + 'allsender', + 'alltype', + 'added', + 'removed', + 'set', + 'replace', + 'to', + 'clear', + 'pendingusercreated', + 'pendinguserrefused', + 'pendinguseraccepted', + 'pendingorgcreated', + 'pendingorgrefused', + 'pendingorgaccepted', + 'roledeleted', + 'roldecreated', + 'orgcreated', + 'orgdeleted', + 'userpasswordchanged', + 'usercreated', + 'userdeleted', + 'system', + 'custom', + 'roleadded', + 'roleremoved', + 'emailrecoverysent', + 'rolecreated' + ].forEach(tr => this.$injector.get('translate')('logs.' + tr, this.i18n)) + // manage query params to get user's or complete logs + let typeQuery = 'Logs' + const params = { + limit: 100000, + page: 0 + } + if (this.user) { + params.id = this.user + typeQuery = 'UserLogs' + } + + this.getCitiesLog = function (log) { + let res = '' + res += log.changed.added ? `${log.changed.added} ${this.i18n.added}${log.changed.new ? `: ${log.changed.new}.` : '.'}` : '' + res += log.changed.removed ? `${log.changed.removed} ${this.i18n.removed}${log.changed.old ? `: ${log.changed.old}.` : '.'}` : '.' + return res + } + + this.getAttrLog = function (log) { + let res = '' + if (log.changed.field === 'logo') { + res += log.changed.new ? this.i18n.set : this.i18n.clear + res = log.changed.old && log.changed.new ? this.i18n.replace : res + } else { + res += log.changed.new && !log.changed.old ? `${this.i18n.set} ${log.changed.new}.` : '' + res += log.changed.old && !log.changed.new ? `${this.i18n.clear} ${log.changed.old}.` : '' + res += log.changed.old && log.changed.new ? `${this.i18n.replace} ${log.changed.old} ${this.i18n.to} ${log.changed.new}.` : '' + } + return res + } + + this.logs = this.$injector.get(typeQuery).query(params, () => { + // transform each logs changed value into json to find info during html construction + this.logs.forEach(l => { + l.changed = JSON.parse(l.changed) + // message + if (l.type === 'EMAIL_SENT') { l.title = 'msg.sent' } + // attributs + if (l.type.indexOf('_ATTRIBUTE_CHANGED') >= 0) { + l.title = (l.changed.field === 'cities') + ? this.getCitiesLog(l) + : this.getAttrLog(l) + l.changed.fieldI18nKey = l.type.split('_').shift().toLowerCase() + '.' + l.changed.field + } else { + l.title = this.i18n[l.type.split('_').join('').toLowerCase()] + } + // role + if (l.type.indexOf('CUSTOM') >= 0 || l.type.indexOf('SYSTEM') >= 0) { + // get type for a role as custom or system + const i18nType = this.i18n[l.type.split('_')[0].toLowerCase()] + // action added or removed + const i18nAction = this.i18n[l.type.split('_').slice(1, 3).join('').toLowerCase()] + l.title = `${i18nType} ${l.changed.field} ${i18nAction} ${l.target}` + } + // get icon name + let iconName = '' + if (l.type.indexOf('CREATED') > -1 || l.type.indexOf('ADDED') > -1) { + iconName = l.type.indexOf('PENDING') > -1 ? 'plus' : 'plus-sign' + } else if (l.type.indexOf('REFUSED') > -1) { + iconName = 'remove' + } else if (l.type.indexOf('ACCEPTED') > -1) { + iconName = 'ok' + } else if (l.type.indexOf('DELETED') > -1 || l.type.indexOf('REMOVED') > -1) { + iconName = 'minus-sign' + } else { + iconName = 'edit' + } + l.icon = iconName + }) + + const extract = (key) => [...new Set(this.logs.map(l => l[key]))] + + this.senders = [{ key: 'all', value: this.i18n.allsender }].concat( + extract('admin').map(g => ({ key: g, value: g })) + ) + this.types = [{ key: 'all', value: this.i18n.alltype }].concat( + extract('type').map(g => ({ key: g, value: g })) + ) + this.targets = [{ key: 'all', value: this.i18n.alltarget }].concat( + extract('target').map(g => ({ key: g, value: g })) + ) + }, () => { + this.$injector.get('Flash').create('danger', this.i18n.error) + }) + + this.target = 'all' + this.type = 'all' + this.admin = 'all' + + this.date = { + start: this.$injector.get('date').getDefault(), + end: this.$injector.get('date').getEnd() + } + + // get all orgs infos and orgs name + this.orgsId = {} + this.$injector.get('Orgs').query(orgs => { + orgs.forEach(org => { + this.orgsId[org.id] = org.name + }) + this.orgs = orgs.map(o => o.name) + }) + + // get all role + this.roles = [] + this.$injector.get('Role').query(roles => { + this.roles = roles.map(role => role.cn) + }) + + // get all users + this.users = [] + this.$injector.get('User').query(users => { + this.users = users.map(user => user.uid) + }) + } + + // get log info and return log target type or empty string + getType (log) { + const type = '' + if (log && this.roles && this.orgsId && this.users && this.orgs) { + if (log.type.indexOf('DELETED') > -1 || log.type.indexOf('REFUSED') > -1) { + // avoid to create link for removed items + return type + } else if (this.roles.indexOf(log.target) > -1) { + return 'ROLE' + } else if (this.orgsId[log.target]) { + return 'ORG' + } else if (this.users.indexOf(log.target) > -1) { + return 'USER' + } + } + return type + } + + isFiltered () { + return this.admin !== 'all' || this.type !== 'all' || this.target !== 'all' || + this.date.start !== this.$injector.get('date').getDefault() || + this.date.end !== this.$injector.get('date').getEnd() + } + + openLog (log) { + // remove old log if not already deleted + if (this.log) { delete this.log } + // get messages for this user + if (log && log.changed) { + if (log.changed.sender) { + // only for mail + log.trusted = this.$injector.get('$sce').trustAsHtml(log.changed.body) + } + this.log = log + } + } + + closeLog () { + // remove log to avoir wrong behavior when log changed + delete this.log + } + + reset () { + this.admin = 'all' + this.type = 'all' + this.target = 'all' + this.date.start = this.$injector.get('date').getDefault() + this.date.end = this.$injector.get('date').getEnd() + } +} + +const filterLogs = () => { + return (logs, type, admin, target, date) => { + if (!logs) { return } + const filtered = logs.filter(log => { + let valid = true + if (type !== 'all' && log.type !== type) { + valid = false + } + if (admin !== 'all' && log.admin !== admin) { + valid = false + } + if (target !== 'all' && log.target !== target) { + valid = false + } + if (date && + (moment(log.date).isBefore(date.start) || + moment(log.date).isAfter(date.end))) { + valid = false + } + return valid + }) + + return filtered + } +} + +const logDateFilter = () => date => moment(date).format('YYYY-MM-DD HH:mm') + +angular.module('manager') + .component('logger', { + bindings: { + filter: '=', + title: '=', + user: '=' + }, + controller: LoggerController, + controllerAs: 'logger', + templateUrl: 'components/logger/logger.tpl.html' + }) + .filter('logs', filterLogs) + .filter('logDate', logDateFilter) diff --git a/console/src/main/webapp/manager/app/components/logger/logger.tpl.html b/console/src/main/webapp/manager/app/components/logger/logger.tpl.html new file mode 100644 index 0000000000..7a06978668 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/logger/logger.tpl.html @@ -0,0 +1,105 @@ +
+ +

logs.title

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + logs.sender + + + logs.target + + + logs.type +
logs.datelogs.senderlogs.targetlogs.type
{{::log.admin}} + + + + + {{::log.target}} + + + + {{::logger.orgsId[log.target]}} + {{::log.target}} + + + + {{::log.target}} + + + {{::log.target}} + + + + {{::'logs.'+log.type.split('_').join('').toLowerCase() | translate}} + + + + + {{::log.changed.subject}} + + + + + {{::log.changed.new || log.changed.old}} + + + + + {{::log.changed.fieldI18nKey | translate}} + + + + {{::log.type + (log.changed.field ? ' (' + log.changed.field + ')' : '')}} + +
+ + +
diff --git a/console/src/main/webapp/manager/app/components/logs/logs.es6 b/console/src/main/webapp/manager/app/components/logs/logs.es6 new file mode 100644 index 0000000000..35a97d3173 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/logs/logs.es6 @@ -0,0 +1,13 @@ +require('components/logs/logs.tpl') + +class LogsController { + static $inject = ['$injector'] + + constructor ($injector) { + this.$injector = $injector + } +} + +angular + .module('manager') + .controller('LogsController', LogsController) diff --git a/console/src/main/webapp/manager/app/components/logs/logs.tpl.html b/console/src/main/webapp/manager/app/components/logs/logs.tpl.html new file mode 100644 index 0000000000..18476e9afc --- /dev/null +++ b/console/src/main/webapp/manager/app/components/logs/logs.tpl.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/console/src/main/webapp/manager/app/components/newUser/newUser.es6 b/console/src/main/webapp/manager/app/components/newUser/newUser.es6 new file mode 100644 index 0000000000..18b7062ca4 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/newUser/newUser.es6 @@ -0,0 +1,39 @@ +require('components/newUser/newUser.tpl') + +require('templates/userForm.tpl') +require('services/translate') +require('services/users') + +class NewUserController { + static $inject = ['$injector', 'translate', 'User'] + + constructor ($injector, translate, User) { + this.$injector = $injector + + this.user = new User({}) + this.i18n = {} + translate('user.created', this.i18n) + translate('user.error', this.i18n) + + this.contexts = $injector.get('Contexts').query() + this.users = User.query() + this.required = $injector.get('UserRequired').get() + } + + save () { + const flash = this.$injector.get('Flash') + const $router = this.$injector.get('$router') + this.user.$save( + () => { + flash.create('success', this.i18n.created) + $router.navigate($router.generate('user', { + id: this.user.uid, + tab: 'infos' + })) + }, + () => { flash.create('danger', this.i18n.error) } + ) + } +} + +angular.module('manager').controller('NewUserController', NewUserController) diff --git a/console/src/main/webapp/manager/app/components/newUser/newUser.tpl.html b/console/src/main/webapp/manager/app/components/newUser/newUser.tpl.html new file mode 100644 index 0000000000..20c2a051fb --- /dev/null +++ b/console/src/main/webapp/manager/app/components/newUser/newUser.tpl.html @@ -0,0 +1,19 @@ +
+ +

+ user.userlist + / + user.new_user +

+ +
+ +
+
+
+
+ + user.discard +
+ +
diff --git a/console/src/main/webapp/manager/app/components/org/org.es6 b/console/src/main/webapp/manager/app/components/org/org.es6 new file mode 100644 index 0000000000..ca4f78592c --- /dev/null +++ b/console/src/main/webapp/manager/app/components/org/org.es6 @@ -0,0 +1,102 @@ +require('components/org/org.tpl') +require('templates/orgForm.tpl') +require('components/area/area') +require('services/orgs') +require('components/imageinput/imageinput') + +class OrgController { + static $inject = ['$injector', '$routeParams'] + + constructor ($injector, $routeParams) { + this.$injector = $injector + + this.q = '' + + this.tabs = ['infos', 'area', 'users', 'manage'] + this.tab = $routeParams.tab + + this.itemsPerPage = 15 + + const translate = $injector.get('translate') + this.i18n = {} + translate('org.updated', this.i18n) + translate('org.error', this.i18n) + translate('org.deleted', this.i18n) + translate('org.deleteError', this.i18n) + translate('org.userremoved', this.i18n) + translate('org.useradded', this.i18n) + translate('user.remove', this.i18n) + + this.org = $injector.get('Orgs').get({ + id: $routeParams.org + }, () => this.loadUsers()) + this.required = $injector.get('OrgsRequired').query() + this.orgTypeValues = $injector.get('OrgsType').query() + + // check if org is under delegation + const Delegations = $injector.get('Delegations') + Delegations.query(resp => { + this.delegations = resp.filter(d => d.orgs.indexOf($routeParams.org) !== -1) + }) + } + + loadUsers () { + const User = this.$injector.get('User') + User.query(users => { + this.users = users.filter(u => u.org === this.org.name) + this.notUsers = users.filter(u => u.org !== this.org.name) + }) + } + + save () { + const flash = this.$injector.get('Flash') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + this.org.$update(() => { + $httpDefaultCache.removeAll() + flash.create('success', this.i18n.updated) + }, flash.create.bind(flash, 'danger', this.i18n.error)) + } + + delete () { + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + const flash = this.$injector.get('Flash') + this.org.$delete(() => { + $httpDefaultCache.removeAll() + const $router = this.$injector.get('$router') + $router.navigate($router.generate('orgs', { id: 'all' })) + flash.create('success', this.i18n.deleted) + }, flash.create.bind(flash, 'danger', this.i18n.deleteError)) + } + + confirm () { + const flash = this.$injector.get('Flash') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + this.org.pending = false + this.org.$update(() => { + $httpDefaultCache.removeAll() + flash.create('success', this.i18n.updated) + }, flash.create.bind(flash, 'danger', this.i18n.error)) + } + + associate (uid, unassociate = false) { + if (!uid) uid = this.user + if (!uid) return + const User = this.$injector.get('User') + const flash = this.$injector.get('Flash') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + + User.update({ + uid: uid, + originalID: uid, + org: unassociate ? '' : this.org.id + }).$promise.then(() => { + $httpDefaultCache.removeAll() + this.loadUsers() + flash.create('success', unassociate ? this.i18n.userremoved : this.i18n.useradded) + }) + } +} + +angular + .module('manager') + .controller('OrgController', OrgController) diff --git a/console/src/main/webapp/manager/app/components/org/org.tpl.html b/console/src/main/webapp/manager/app/components/org/org.tpl.html new file mode 100644 index 0000000000..1ddb7b4326 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/org/org.tpl.html @@ -0,0 +1,106 @@ +
+ +

+ org.orglist + / + {{::org.org.name}} +

+ +
+ + + +
+ +
+ +
+ org.ohasdeleg + + {{ delegation.uid }}{{$last ? '' : ', '}} + +
+ +
+ org.pendingmsg + +
+ +
+
+
+
+ +
+
+ +
+ +
+ +
+ + +

+ +

+
+
+ + +
+
+ + + + + + + + + + + + + + + + + +
users.useruser.action
+ {{::user.sn}} {{::user.givenName}} + + + + +
+ + + +
+ +
+
+ + org.warning + +
+
+ +
+ +
diff --git a/console/src/main/webapp/manager/app/components/orgs/orgs.es6 b/console/src/main/webapp/manager/app/components/orgs/orgs.es6 new file mode 100644 index 0000000000..4005e13e7a --- /dev/null +++ b/console/src/main/webapp/manager/app/components/orgs/orgs.es6 @@ -0,0 +1,88 @@ +require('components/orgs/orgs.tpl') +require('services/orgs') + +class OrgsController { + static $inject = ['$injector', '$routeParams'] + + constructor ($injector, $routeParams) { + this.$injector = $injector + + this.org = $routeParams.org + this.orgs = this.$injector.get('Orgs').query(() => { + if (this.org === 'pending') { + this.orgs = this.orgs.filter(o => o.pending) + } else { + // display no pendings orgs + this.orgs = this.orgs.filter(o => !o.pending) + } + this.orgs.forEach(org => { + org.membersCount = org.members.length + delete org.members + }) + }) + + this.q = '' + this.itemsPerPage = 15 + + this.newOrg = this.$injector.get('$location').$$search.new === 'org' + if (this.newOrg) { + const Org = this.$injector.get('Orgs') + this.newInstance = new Org({}) + } + + this.required = $injector.get('OrgsRequired').query() + this.orgTypeValues = $injector.get('OrgsType').query() + + const translate = this.$injector.get('translate') + this.i18n = {} + translate('org.created', this.i18n) + translate('org.updated', this.i18n) + translate('org.deleted', this.i18n) + translate('org.error', this.i18n) + translate('org.deleteError', this.i18n) + } + + create () { + const Org = this.$injector.get('Orgs') + this.newInstance = new Org({}) + const $location = this.$injector.get('$location') + $location.search('new', 'org') + } + + saveOrg () { + const flash = this.$injector.get('Flash') + const $router = this.$injector.get('$router') + const $location = this.$injector.get('$location') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + + this.newInstance.$save( + () => { + flash.create('success', this.i18n.created) + $httpDefaultCache.removeAll() + $router.navigate($router.generate('org', { + org: this.newInstance.id, + tab: 'infos' + })) + $location.url($location.path()) + }, + flash.create.bind(flash, 'danger', this.i18n.error) + ) + } + + close () { + this.newOrg = false + const $location = this.$injector.get('$location') + $location.url($location.path()) + } + + activate ($scope) { + const $location = this.$injector.get('$location') + $scope.$watch(() => $location.search().new, (v) => { + this.newOrg = v === 'org' + }) + } +} + +OrgsController.prototype.activate.$inject = ['$scope'] + +angular.module('manager').controller('OrgsController', OrgsController) diff --git a/console/src/main/webapp/manager/app/components/orgs/orgs.tpl.html b/console/src/main/webapp/manager/app/components/orgs/orgs.tpl.html new file mode 100644 index 0000000000..2fe8d58e0f --- /dev/null +++ b/console/src/main/webapp/manager/app/components/orgs/orgs.tpl.html @@ -0,0 +1,80 @@ +
+ +
+ + + +
+ +
+ + + +

+ +

+ + + + + + + + + + + + + + + + + + +
org.nameorg.shortNameorg.membersCount
+ + {{::org.name}} + + {{::org.shortName}}{{::org.membersCount}}
+ + + +
+ +
+ +
+ +
+ × + orgs.neworg +
+
+
+ +
+ +
+ + +
+
+ +
+ +
+ +
diff --git a/console/src/main/webapp/manager/app/components/role/role.es6 b/console/src/main/webapp/manager/app/components/role/role.es6 new file mode 100644 index 0000000000..9b5d73f700 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/role/role.es6 @@ -0,0 +1,104 @@ +import 'components/role/role.tpl' +import 'templates/roleForm.tpl' +import 'services/roles' + +class RoleController { + static $inject = ['$injector', '$routeParams'] + + constructor ($injector, $routeParams) { + this.$injector = $injector + + this.q = '' + + this.tabs = ['infos', 'users', 'manage'] + this.tab = $routeParams.tab + + this.itemsPerPage = 15 + + const translate = $injector.get('translate') + this.i18n = {} + translate('role.updated', this.i18n) + translate('role.error', this.i18n) + translate('role.deleted', this.i18n) + translate('role.deleteError', this.i18n) + translate('role.userremoved', this.i18n) + translate('role.useradded', this.i18n) + translate('user.remove', this.i18n) + + this.loadRoleAndUsers($routeParams.role) + } + + loadRoleAndUsers (id) { + // Save original cn in case we change cn, because we need to + // use original cn with PUT request + this.role = this.$injector.get('Role').get({ id: id }, role => { + role.originalID = role.cn + }) + + this.role.$promise.then(() => { + const User = this.$injector.get('User') + User.query(users => { + this.users = users.filter(u => this.role.users.indexOf(u.uid) >= 0) + this.notUsers = users.filter(u => this.role.users.indexOf(u.uid) === -1) + }) + }) + } + + save () { + const flash = this.$injector.get('Flash') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + const $router = this.$injector.get('$router') + this.role.$update(() => { + $httpDefaultCache.removeAll() + this.role.originalID = this.role.cn + flash.create('success', this.i18n.updated) + $router.navigate($router.generate('role', { + role: this.role.cn, + tab: 'infos' + })) + }, flash.create.bind(flash, 'danger', this.i18n.error)) + } + + delete () { + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + const flash = this.$injector.get('Flash') + this.role.$delete(() => { + $httpDefaultCache.removeAll() + const $router = this.$injector.get('$router') + $router.navigate($router.generate('roles', { id: 'all' })) + flash.create('success', this.i18n.deleted) + }, flash.create.bind(flash, 'danger', this.i18n.deleteError)) + } + + confirm () { + const flash = this.$injector.get('Flash') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + this.role.status = 'REGISTERED' + this.role.$update(() => { + $httpDefaultCache.removeAll() + flash.create('success', this.i18n.updated) + }, flash.create.bind(flash, 'danger', this.i18n.error)) + } + + associate (uid, unassociate = false) { + if (!uid) uid = this.user + if (!uid) return + const flash = this.$injector.get('Flash') + const RolesUsers = this.$injector.get('RolesUsers') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + + RolesUsers.save({ + users: [uid], + PUT: unassociate ? [] : [this.role.cn], + DELETE: unassociate ? [this.role.cn] : [] + }, () => { + $httpDefaultCache.removeAll() + this.loadRoleAndUsers(this.role.cn) + flash.create('success', unassociate ? this.i18n.userremoved : this.i18n.useradded) + }, () => { + flash.create('danger', 'FAIL') + }) + } +} + +angular.module('manager').controller('RoleController', RoleController) diff --git a/console/src/main/webapp/manager/app/components/role/role.tpl.html b/console/src/main/webapp/manager/app/components/role/role.tpl.html new file mode 100644 index 0000000000..fa75133c9c --- /dev/null +++ b/console/src/main/webapp/manager/app/components/role/role.tpl.html @@ -0,0 +1,91 @@ +
+ +

+ role.rolelist + / + {{::role.role.cn}} +

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

+ +

+
+
+ + +
+
+ + + + + + + + + + + + + + + + + +
users.useruser.action
+ {{::user.sn}} {{::user.givenName}} + + + + +
+ + + +
+ +
+
+ + role.warning + +
+
+ +
+ +
diff --git a/console/src/main/webapp/manager/app/components/roles/roles.es6 b/console/src/main/webapp/manager/app/components/roles/roles.es6 new file mode 100644 index 0000000000..c44737dd08 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/roles/roles.es6 @@ -0,0 +1,79 @@ +import 'components/roles/roles.tpl' +import 'services/roles' + +class RolesController { + static $inject = ['$injector', '$routeParams'] + + constructor ($injector, $routeParams) { + this.$injector = $injector + + this.role = $routeParams.role + this.roles = this.$injector.get('Role').query(() => { + this.roles.forEach(r => { + r.usersCount = r.users.length + delete r.users + }) + }) + + this.q = '' + this.itemsPerPage = 15 + + this.newRole = this.$injector.get('$location').$$search.new === 'role' + if (this.newRole) { + const Role = this.$injector.get('Role') + this.newInstance = new Role({}) + } + + const translate = this.$injector.get('translate') + this.i18n = {} + translate('role.created', this.i18n) + translate('role.updated', this.i18n) + translate('role.deleted', this.i18n) + translate('role.error', this.i18n) + translate('role.deleteError', this.i18n) + } + + create () { + const Role = this.$injector.get('Role') + this.newInstance = new Role({}) + const $location = this.$injector.get('$location') + $location.search('new', 'role') + } + + saveRole () { + const flash = this.$injector.get('Flash') + const $router = this.$injector.get('$router') + const $location = this.$injector.get('$location') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + + this.newInstance.$save( + () => { + flash.create('success', this.i18n.created) + $httpDefaultCache.removeAll() + $router.navigate($router.generate('role', { + role: this.newInstance.cn, + tab: 'infos' + })) + $location.url($location.path()) + }, + flash.create.bind(flash, 'danger', this.i18n.error) + ) + } + + close () { + this.newRole = false + const $location = this.$injector.get('$location') + $location.url($location.path()) + } + + activate ($scope) { + const $location = this.$injector.get('$location') + $scope.$watch(() => $location.search().new, (v) => { + this.newRole = v === 'role' + }) + } +} + +RolesController.prototype.activate.$inject = ['$scope'] + +angular.module('manager').controller('RolesController', RolesController) diff --git a/console/src/main/webapp/manager/app/components/roles/roles.tpl.html b/console/src/main/webapp/manager/app/components/roles/roles.tpl.html new file mode 100644 index 0000000000..639a45ffce --- /dev/null +++ b/console/src/main/webapp/manager/app/components/roles/roles.tpl.html @@ -0,0 +1,79 @@ +
+ +
+ + + +
+ +
+ + + +

+ +

+ + + + + + + + + + + + + + + + + +
role.cnrole.membersCount
+ + {{::role.cn}} + +   + {{::role.usersCount}}
+ + + +
+ +
+ +
+ +
+ × + roles.newrole +
+
+
+ +
+ +
+ + +
+
+ +
+ +
+ +
diff --git a/console/src/main/webapp/manager/app/components/stats/stats.es6 b/console/src/main/webapp/manager/app/components/stats/stats.es6 new file mode 100644 index 0000000000..8fbe5b3a3f --- /dev/null +++ b/console/src/main/webapp/manager/app/components/stats/stats.es6 @@ -0,0 +1,161 @@ +require('components/stats/stats.tpl') + +class StatsController { + static $inject = ['$element', '$scope', '$injector'] + + constructor ($element, $scope, $injector) { + this.$injector = $injector + this.$element = $element + this.$scope = $scope + } + + $onInit () { + const initialize = this.initialize.bind(this) + if (this.data) { + this.data.$promise.then(initialize) + } + + this.$scope.$watch('stats.data', (newVal, oldVal) => { + if (oldVal !== newVal) { + newVal.$promise.then(initialize) + } + }) + } + + initialize () { + const $element = this.$element + + var options + + this.parseData() + this.granularity = this.data.granularity + + if (this.type === 'bar') { + options = { + seriesBarDistance: 10, + reverseData: true, + horizontalBars: true, + axisY: { + offset: 200 + }, + axisX: { + labelInterpolationFnc: (value, index) => { + if (value > 1000000 && index % 2 === 0) { return null } + if (value >= 10000) { return Math.floor(value / 1000) + 'K' } + return value + } + } + } + } else { + const formatDay = v => { + const splits = v.split('-').reverse() + splits.pop() + return splits.join('/') + } + options = { + fullWidth: true, + axisY: { + offset: 45, + labelInterpolationFnc: (value, index) => + (value > 10000) ? Math.floor(value / 100) / 10 + 'K' : value + }, + axisX: { + labelInterpolationFnc: (value, index) => { + if (this.granularity === 'HOUR') { + return value.split(' ')[1] + 'H' + } + if (this.granularity === 'DAY' && this.parsed.series[0].length > 8) { + return (parseInt(value.split('-')[2]) % 4 === 1) + ? formatDay(value) : null + } + if (this.granularity === 'DAY') { + return formatDay(value) + } + if (this.granularity === 'WEEK') { + return (parseInt(value.split('-')[1]) % 2 === 0) ? value : null + } + if (this.granularity === 'MONTH') { + return (parseInt(value.split('-')[1]) % 3 === 1) ? value : null + } + return value + } + } + + } + } + const el = $element.find('.chartist') + this.lines = new Chartist[this.type === 'bar' ? 'Bar' : 'Line']( + el[0], this.parsed, options + ) + + // Replace foreign object with text tag to allow png export. + // We then have to correctly place labels by ourselves. + this.lines.on('draw', (data) => { + if (data.type === 'label') { + // Move x-axis label above bottom line + let ydiff = 8 + if (data.axis.units.dir === 'vertical') { + // Align y-axis labels in front of lines + const delta = el.height() / (data.axis.ticks.length) + // For bar graph, move it in front of bar + ydiff = (this.type === 'bar') ? 18 : delta + } + const text = Chartist.Svg('text', { + x: data.x, + y: data.y + ydiff + }).text(data.text) + data.element.replace(text) + } + }) + this.$injector.get('translate')(this.title).then( + (v) => el.attr('title', v) + ) + this.view = 'graph' + + this.exportPNG = () => { + const el = $element.find('svg') + el.append($( + '') + ) + saveSvgAsPng(el[0], 'image.png') + } + + this.exportCSV = () => { + this.$injector.get('Analytics').download(this.csvConfig).$promise.then((data) => { + window.saveAs(data.response.blob, 'document.csv') + }) + } + } + + switchView () { + this.view = (this.view === 'graph') ? 'table' : 'graph' + } + + parseData () { + const data = this.data.results + this.nodata = !data || data.length === 0 + if (this.nodata) { return } + const serie = data.map(x => x[this.config[1]]) + this.serie = (this.type === 'line') ? [].concat(serie).reverse() : serie + this.parsed = { + labels: data.map(x => x[this.config[0]]), + series: [[].concat(serie)] + } + } +} + +angular.module('manager') + .component('stats', { + bindings: { + data: '=', + type: '=', + config: '=', + title: '=', + csvConfig: '=' + }, + controller: StatsController, + controllerAs: 'stats', + templateUrl: 'components/stats/stats.tpl.html' + }) diff --git a/console/src/main/webapp/manager/app/components/stats/stats.tpl.html b/console/src/main/webapp/manager/app/components/stats/stats.tpl.html new file mode 100644 index 0000000000..f13eff4b1e --- /dev/null +++ b/console/src/main/webapp/manager/app/components/stats/stats.tpl.html @@ -0,0 +1,51 @@ +
+ +
+ + +
+ +

+ {{stats.title | translate}} + + {{'analytics.' + stats.data.granularity | translate}} + +

+ +
+   + analytics.nodata +
+ +
+
+
+
+ +
+ + + + + + + +
{{stats.parsed.labels[$index]}}{{stats.parsed.labels[stats.serie.length - $index -1]}}{{value}}
+ + + + + + + +
diff --git a/console/src/main/webapp/manager/app/components/user/user.es6 b/console/src/main/webapp/manager/app/components/user/user.es6 new file mode 100644 index 0000000000..45da81e5a7 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/user/user.es6 @@ -0,0 +1,453 @@ +require('components/user/user.tpl') + +require('services/util') +require('services/contexts') + +class UserController { + static $inject = ['$routeParams', '$injector', '$location', 'User', 'Role', 'Orgs'] + + constructor ($routeParams, $injector, $location, User, Role, Orgs) { + this.$injector = $injector + + this.TMP_ROLE = this.$injector.get('temporaryRole') + this.EXPIRED_ROLE = this.$injector.get('expiredRole') + + const translate = $injector.get('translate') + this.i18n = {} + const strings = [ + 'user.updated', 'user.error', 'user.deleted', 'user.content', + 'org.select', 'delegation.dupdated', 'delegation.ddeleted'] + strings.map(str => translate(str, this.i18n)) + + this.tabs = ['infos', 'roles', 'analytics', 'messages', 'logs', 'manage'] + this.tab = $routeParams.tab + this.uid = $routeParams.id + + this.user = User.get({ id: this.uid }, (user) => { + user.originalID = user.uid + if (user.org && user.org !== '') { + user.orgObj = Orgs.get({ id: user.org }, org => { + user.validOrg = !org.pending + }) + } else { + user.validOrg = true + } + if (this.tab === 'delegations') { + const Delegations = $injector.get('Delegations') + Delegations.query(resp => { + const deleg = resp.find(x => x.uid === this.user.uid) + const options = deleg || { orgs: [], roles: [], uid: this.user.uid } + this.delegation = new Delegations(options) + this.activeDelegation = this.hasDelegation() + $injector.get('Orgs').query(orgs => { + this.orgs = orgs.filter(o => !o.pending) + }) + }) + } + if (this.tab === 'messages') { + this.messages = this.$injector.get('Email').query({ id: this.user.uid }, r => { + if ($location.$$path.indexOf('msgid') === -1) return + const msgid = new URLSearchParams($location.$$path.split('?').pop()).get('msgid') + r.emails.forEach(m => { + if (m.id.toString() === msgid) this.openMessage(m) + }) + }) + } + }) + this.adminRoles = this.$injector.get('roleAdminList')() + switch (this.tab) { + case 'infos': + this.contexts = $injector.get('Contexts').query() + break + case 'messages': + this.templates = this.$injector.get('Templates').query() + this.attachments = this.$injector.get('Attachments').query() + break + default: + } + this.bindRoles() + + this.required = $injector.get('UserRequired').get() + } + + hasDelegation () { + if (!this.delegation) return false + return (this.delegation.orgs.length !== 0) && (this.delegation.roles.length !== 0) + } + + // search each choosen span elements and set title manually + // to display roles description on hover + setTitles () { + if (this.roleDescriptions) { + [].forEach.call( + document.querySelectorAll('li.search-choice span'), + span => span.setAttribute('title', this.roleDescriptions[span.innerHTML]) + ) + } + } + + bindRoles () { + // Load role infos for every tab (for confirmation) + const Role = this.$injector.get('Role') + this.roles = Role.query(roles => { + this.allroles = roles.map(r => r.cn) + // get roles informations to get description from template + this.roleDescriptions = {} + roles.map(r => { + this.roleDescriptions[r.cn] = r.description + // Check if user is expired + if (r.cn === this.EXPIRED_ROLE) { + this.user.$promise.then(user => { + user.expired = r.users.indexOf(user.uid) >= 0 + }) + } + }) + }) + this.user.$promise.then(() => { + const roleAdminFilter = this.$injector.get('roleAdminFilter') + const notAdmin = [] + this.$injector.get('$q').all([ + this.user.$promise, + this.roles.$promise + ]).then(() => { + this.user.roles = this.user.roles || [] + this.user.adminRoles = this.user.adminRoles || {} + this.roles.forEach((role) => { + if (role.users.indexOf(this.user.uid) >= 0) { + if (roleAdminFilter(role)) { + this.user.adminRoles[role.cn] = true + } else { + this.user.roles.push(role.cn) + } + } + if (!roleAdminFilter(role) && role.cn !== this.TMP_ROLE) { + notAdmin.push(role.cn) + } + }) + this.roles = notAdmin + }) + }) + } + + loadAnalytics ($scope) { + const date = this.$injector.get('date') + + this.date = { + start: date.getFromDiff('year'), + end: date.getEnd() + } + + this.config = { + layers: ['layer', 'count'], + requests: ['date', 'count'], + extractions: ['layer', 'count'] + } + this.loadAnalyticsData() + } + + loadAnalyticsData () { + const i18n = {} + const i18nPromise = this.$injector.get('translate')('analytics.errorload', i18n) + const flash = this.$injector.get('Flash') + + this.$injector.get('$q').all([ + this.user.$promise, + i18nPromise + ]).then(() => { + const error = flash.create.bind(flash, 'danger', i18n.errorload) + const Analytics = this.$injector.get('Analytics') + const options = { + service: 'combinedRequests.json', + user: this.user.uid, + startDate: this.date.start, + endDate: this.date.end + } + this.requests = Analytics.get(options, () => {}, error) + + const usageOptions = { + ...options, + service: 'layersUsage.json', + limit: 10 + } + this.layers = Analytics.get(usageOptions, () => {}, error) + + this.usageOptions = { ...usageOptions } + delete this.usageOptions.limit + this.usageOptions.service = 'layersUsage.csv' + + const extractionOptions = { + ...options, + service: 'layersExtraction.json', + limit: 10 + } + + this.extractions = Analytics.get(extractionOptions, () => {}, error) + this.extractionOptions = { ...extractionOptions } + delete this.extractionOptions.limit + this.extractionOptions.service = 'layersExtraction.csv' + }) + } + + save () { + const flash = this.$injector.get('Flash') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + const $router = this.$injector.get('$router') + this.user.$update(() => { + $httpDefaultCache.removeAll() + this.user.originalID = this.user.uid + flash.create('success', this.i18n.updated) + // To update URI if uid has changed + $router.navigate($router.generate('user', { + id: this.user.uid, + tab: 'infos' + })) + }, flash.create.bind(flash, 'danger', this.i18n.error)) + } + + delete () { + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + const flash = this.$injector.get('Flash') + this.user.$delete(() => { + $httpDefaultCache.removeAll() + const $router = this.$injector.get('$router') + $router.navigate($router.generate('users', { id: 'all' })) + flash.create('success', this.i18n.deleted) + }, flash.create.bind(flash, 'danger', this.i18n.error)) + } + + initCompose () { + this.quill = new Quill(document.querySelector('#compose_content'), { + modules: { + toolbar: [ + [{ header: [1, 2, false] }], + ['bold', 'italic', 'underline', 'image', { color: [] }, { align: [] }] + ] + }, + placeholder: this.i18n.content, + theme: 'snow' + }) + this.quill.on('text-change', () => { + this.compose.content = this.quill.container.firstChild.innerHTML + }) + } + + openMessage (message) { + const $router = this.$injector.get('$router') + $router.navigate($router.generate('user', { id: this.user.uid, tab: 'messages', queryParams: { msgid: message.id } })) + message.trusted = this.$injector.get('$sce').trustAsHtml(message.body) + this.message = message + } + + closeMessage (message) { + const $router = this.$injector.get('$router') + $router.navigate($router.generate('user', { id: this.user.uid, tab: 'messages' })) + delete this.message + delete this.compose + } + + loadTemplate () { + this.compose.subject = this.compose.template.name + this.quill.setText(this.compose.template.content) + } + + sendMail () { + const flash = this.$injector.get('Flash') + const Mail = this.$injector.get('Mail') + const i18n = {} + this.$injector.get('translate')('msg.sent', i18n) + this.$injector.get('translate')('msg.error', i18n) + const attachments = [] + for (const attachId in this.compose.attachments) { + if (this.compose.attachments[attachId]) { attachments.push(attachId) } + } + (new Mail({ + id: this.user.uid, + subject: this.compose.subject, + content: this.compose.content, + attachments: attachments.join(',') + })).$save((r) => { + delete this.compose + flash.create('success', i18n.sent) + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + $httpDefaultCache.removeAll() + this.messages = this.$injector.get('Email').query({ id: this.user.uid }) + }, () => { flash.create('danger', i18n.error) }) + } + + confirm () { + this.user.pending = false + this.save() + } + + deleteDelegation () { + const flash = this.$injector.get('Flash') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + this.delegation.$delete(() => { + $httpDefaultCache.removeAll() + flash.create('success', this.i18n.ddeleted) + this.delegation = new (this.$injector.get('Delegations'))({ + uid: this.user.uid, roles: [], orgs: [] + }) + this.activeDelegation = false + }, flash.create.bind(flash, 'danger', this.i18n.derror)) + } + + saveDelegation () { + const flash = this.$injector.get('Flash') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + this.delegation.$update(() => { + $httpDefaultCache.removeAll() + flash.create('success', this.i18n.dupdated) + this.activeDelegation = this.hasDelegation() + }, flash.create.bind(flash, 'danger', this.i18n.derror)) + } + + activate ($scope) { + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + const flash = this.$injector.get('Flash') + + $scope.$watch(() => $scope.profile, p => { + if (p !== 'SUPERUSER' || this.tabs.indexOf('delegations') !== -1) return + this.tabs.splice(3, 0, 'delegations') + }) + + const saveRoles = function (newVal, oldVal) { + if (!newVal || !oldVal) { return } + const removeTmp = g => g !== this.TMP_ROLE + newVal = newVal.filter(removeTmp) + oldVal = oldVal.filter(removeTmp) + + const toPut = newVal.filter(a => oldVal.indexOf(a) === -1) + const toDel = oldVal.filter(a => newVal.indexOf(a) === -1) + + if (toPut.length === 0 && toDel.length === 0) { return } + if (toPut.length > 1 && toDel.length === 0) { return } // Wrong artifacts + + const i18n = {} + this.$injector.get('translate')('users.roleUpdated', i18n) + this.$injector.get('translate')('users.roleUpdateError', i18n) + + this.rolePromise = this.$injector.get('RolesUsers').save( + { + users: [this.user.uid], + PUT: toPut, + DELETE: toDel + }, + () => { + flash.create('success', i18n.roleUpdated) + $httpDefaultCache.removeAll() + }, + () => { + flash.create('danger', i18n.roleUpdateError) + } + ) + } + + this.$injector.get('$q').all([ + this.user.$promise, + this.roles.$promise + ]).then(() => { + $scope.$watch(() => this.user.roles, saveRoles.bind(this)) + + let previousRoles + $scope.$watchCollection(() => { + const roles = [] + for (const g in this.user.adminRoles) { + if (this.user.adminRoles[g]) { roles.push(g) } + } + if (this.user.adminRoles) { + previousRoles = roles + // to manually display roles description on roles multi select elements + this.setTitles() + return roles + } else { + return previousRoles + } + }, saveRoles.bind(this)) + }) + + if (this.tab === 'analytics') { + this.loadAnalytics($scope) + } + } + + isUnassignableRole (role) { + return this.$injector.get('readonlyRoleList').includes(role) + } +} + +UserController.prototype.activate.$inject = ['$scope'] + +angular.module('manager') + .controller('UserController', UserController) + .filter('encodeURIComponent', () => window.encodeURIComponent) + .directive('managers', ['$timeout', 'User', ($timeout, User) => ({ + link: (scope, elm, attrs, ctrl) => { + const promise = scope.$eval(attrs.promise) + const selUsers = [] + User.query((users) => { + users.map((u) => { + const id = u.uid + selUsers.push({ + id: id, + text: (u.sn || '') + ' ' + (u.givenName || '') + }) + }) + elm.select2({ + placeholder: '', + allowClear: true, + data: selUsers + }) + const cb = () => { $timeout(() => { elm.trigger('change') }) } + if (promise) { + promise.then(cb) + } else { + cb() + } + }) + } + })]) + .directive('organizations', ['$timeout', '$router', 'Orgs', ($timeout, $router, Orgs) => ({ + link: (scope, elm, attrs, ctrl) => { + const promise = scope.$eval(attrs.promise) + const user = scope.$eval(attrs.model) + + // Initialize pending value for new user + if (user.pending === undefined) { + user.pending = false + } + const selOrgs = [] + Orgs.query((orgs) => { + orgs.forEach((o) => { + if (user.pending || !o.pending) { + selOrgs.push({ + id: o.id, + text: o.name + }) + } + }) + // create template to format selected element + const formatSelected = (state) => { + if (!state.id) return state.text + const route = $router.generate('org', { org: state.id, tab: 'infos' }) + return $(`${state.text}`) + } + elm.select2({ + templateSelection: formatSelected, + placeholder: '', + allowClear: true, + data: selOrgs + }) + const cb = () => { + $timeout(() => { + elm.trigger('change') + }) + } + if (promise) { + promise.then(cb) + } else { + cb() + } + }) + } + })]) diff --git a/console/src/main/webapp/manager/app/components/user/user.tpl.html b/console/src/main/webapp/manager/app/components/user/user.tpl.html new file mode 100644 index 0000000000..54318f5132 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/user/user.tpl.html @@ -0,0 +1,236 @@ +
+ +

+ user.userlist + / + {{::user.user.sn}} {{::user.user.givenName}} +

+ +
+ + + +
+
+ +
+ user.pendingmsg + + + — + user.orgFirst + + {{::user.user.orgObj.name || user.user.orgObj.shortName}} + + +
+ +
+ user.expiredmsg : +
+ +
+ +
+
+ +
+ +
+ +
+ +
+ +

role.system

+
+
+
+ +
+
+
+ +
+ +

role.app

+
+ +
+
+ +
+ user.hasdeleg + user.nodeleg +
+ +
+

delegation.restricted

+
+ +
+

user.manages_roles

+
+ +
+

user.manages_orgs

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

analytics.title

+
+
+ + +
+
+
+ +
+
+ +
+ + + + +
+
+ +
+ +
+
+
+ +
+
+ +
msg.attachments
+
+
+
+ +
+
+
+ +
+ +
+ + +
+ +
+
+

+ + {{user.message.sender}}, msg.on {{user.message.date | date: 'mediumDate'}} + +

+

+ {{user.message.subject}} +

+

+
+
msg.attachments
+
    +
  • + {{attachment.name}} +
  • +
+
+ + + + + + + + + + + + + + + + + + +
msg.datemsg.subjectmsg.sender
{{::message.date | date: 'mediumDate'}} + + {{::message.subject}}msg.nosubject + + {{::message.sender}}
+
msg.empty
+
+
+ +
+

user.logs

+ + +
+ +
+
+ + user.warning + +
+
+ +
+
+ +
diff --git a/console/src/main/webapp/manager/app/components/users/users.es6 b/console/src/main/webapp/manager/app/components/users/users.es6 new file mode 100644 index 0000000000..6de0c52b0d --- /dev/null +++ b/console/src/main/webapp/manager/app/components/users/users.es6 @@ -0,0 +1,172 @@ +require('components/users/users.tpl') + +require('services/users') +require('services/roles_users') +require('services/logs') +require('services/messages') + +class UsersController { + static $inject = ['$routeParams', '$injector', 'User', 'Role'] + + constructor ($routeParams, $injector, User, Role) { + this.$injector = $injector + + this.q = '' + this.itemsPerPage = 25 + this.selection = [] + this.filterSelected = false + + this.newRole = this.$injector.get('$location').$$search.new === 'role' + this.newRoleName = '' + + this.users = User.query(() => { + this.allUsers = this.users.slice() + }) + + const active = $routeParams.id + + this.roles = Role.query() + this.activePromise = this.roles.$promise.then(() => { + this.activeRole = this.roles.filter(g => g.cn === active)[0] + if (active === 'pending') { + this.activeRole = { + cn: 'PENDING', + description: 'users.pending_desc' + } + } + // filter users + this.filter(this.activeRole) + return this.activeRole + }) + this.selectionFilter = this.selectionFilter.bind(this) + } + + filter (role) { + this.users.$promise.then(() => { + // Display no pending users by default + this.users = this.allUsers.filter(user => !user.pending) + // Special case for pending + if (role && role.cn === 'PENDING') { + this.users = this.allUsers.filter(user => user.pending) + return + } + this.users = this.allUsers.filter( + user => (role.users.indexOf(user.uid) >= 0) + ) + }) + } + + toggleSelected (uid) { + if (this.selection.indexOf(uid) >= 0) { + this.selection = this.selection.filter(id => id !== uid) + } else { + this.selection.push(uid) + } + } + + select (sel) { + const filter = this.$injector.get('$filter')('filter') + switch (sel) { + case 'all': + this.selection = filter(this.users, this.q).map(u => u.uid) + break + case 'none': + this.selection = [] + break + } + } + + selectionFilter (u) { + return (this.filterSelected) + ? this.selection.indexOf(u.uid) >= 0 + : true + } + + export_ (fileType) { + const download = this.$injector.get(`Export${fileType.toUpperCase()}`) + download(this.selection).then(result => { + if (result.status !== 200) { + throw new Error(`Cannot fetch users list. error ${result.status}`) + } + let mimetype = '' + switch (fileType) { + case 'vcf': + mimetype = 'text/x-vcard' + break + default: + mimetype = `text/${fileType}` + } + const blob = new Blob(['\ufeff', result.data], { type: mimetype }) + const a = document.createElement('a') + a.href = window.URL.createObjectURL(blob) + a.target = '_blank' + const filter = this.$injector.get('$filter') + const date = filter('date')(new Date(), 'yyyyMMdd-HHmmss') + a.download = `${date}_users_export.${fileType}` + document.body.appendChild(a) // create the link "a" + a.click() // click the link "a" + document.body.removeChild(a) + }).catch(err => { + const flash = this.$injector.get('Flash') + flash.create('danger', err) + }) + } + + exportCSV () { + this.export_('csv') + } + + exportVCF () { + this.export_('vcf') + } + + close () { + this.newRole = false + this.newRoleName = '' + const $location = this.$injector.get('$location') + $location.url($location.path()) + } + + saveRole () { + const flash = this.$injector.get('Flash') + const $router = this.$injector.get('$router') + const $location = this.$injector.get('$location') + const $httpDefaultCache = this.$injector.get('$cacheFactory').get('$http') + + const role = new (this.$injector.get('Role'))() + role.cn = this.newRoleName + role.description = this.newRoleDesc + + role.$save( + () => { + flash.create('success', this.i18n.created) + $httpDefaultCache.removeAll() + $router.navigate($router.generate('users', { id: role.cn })) + $location.url($location.path()) + }, + flash.create.bind(flash, 'danger', this.i18n.error) + ) + } + + activate ($scope) { + const $location = this.$injector.get('$location') + $scope.$watch(() => $location.search().new, (v) => { + this.newRole = v === 'role' + }) + } +} + +UsersController.prototype.activate.$inject = ['$scope'] + +angular.module('manager') + .controller('UsersController', UsersController) + .directive('validateRole', () => ({ + require: 'ngModel', + link: (scope, elm, attrs, ctrl) => { + ctrl.$validators.validateRole = (modelValue, viewValue) => { + const roles = scope.$eval(attrs.validateRole) + const prefix = viewValue.substr(0, viewValue.lastIndexOf('_')) + return prefix === '' || roles.some(g => g.cn === prefix) + } + } + })) diff --git a/console/src/main/webapp/manager/app/components/users/users.tpl.html b/console/src/main/webapp/manager/app/components/users/users.tpl.html new file mode 100644 index 0000000000..14c9cfb3c4 --- /dev/null +++ b/console/src/main/webapp/manager/app/components/users/users.tpl.html @@ -0,0 +1,76 @@ +
+ + + +
+ + + +

+ +

+

+ {{('users.'+users.activeRole.cn | translate).replace('users.', '')}} +

+

+ {{users.activeRole.description | translate}} +

+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
users.userusers.loginusers.organizationusers.email
+ + + {{::user.sn}} {{::user.givenName}} + {{::user.uid}} + {{::user.org}} + {{::user.mail}}
+ + + + +
+ +
diff --git a/console/src/main/webapp/manager/app/services/analytics.es6 b/console/src/main/webapp/manager/app/services/analytics.es6 new file mode 100644 index 0000000000..660652d044 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/analytics.es6 @@ -0,0 +1,35 @@ +angular.module('manager').factory('Analytics', [ + '$resource', 'ANALYTICS_SERVICES_PATH', ($resource, baseUri) => $resource( + baseUri + ':service', { service: '@service' }, + { + get: { + method: 'POST', + cache: true, + isArray: false + }, + download: { + method: 'POST', + headers: { + accept: 'application/csv' + }, + responseType: 'arraybuffer', + cache: false, + transformResponse: (data, headers) => { + let csv = null + if (data) { + csv = new Blob([data], { + type: 'application/csv' + }) + } + + return { + response: { + blob: csv, + fileName: 'export.csv' + } + } + } + } + } + ) +]) diff --git a/console/src/main/webapp/manager/app/services/contexts.es6 b/console/src/main/webapp/manager/app/services/contexts.es6 new file mode 100644 index 0000000000..8ee863cda9 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/contexts.es6 @@ -0,0 +1,9 @@ +angular.module('manager') + .factory('Contexts', ['$resource', 'VIEWER_SERVICES_PATH', ($resource, baseUri) => + $resource(baseUri + 'contexts', {}, { + query: { + cache: true, + isArray: true + } + }) + ]) diff --git a/console/src/main/webapp/manager/app/services/delegations.es6 b/console/src/main/webapp/manager/app/services/delegations.es6 new file mode 100644 index 0000000000..b2513ecec1 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/delegations.es6 @@ -0,0 +1,23 @@ +angular.module('manager') + .factory('Delegations', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + 'delegation/delegations', {}, { + query: { + cache: true, + method: 'GET', + isArray: true + }, + get: { + isArray: false + }, + update: { + url: baseUri + 'delegation/:uid', + params: { uid: '@uid' }, + method: 'POST' + }, + delete: { + url: baseUri + 'delegation/:uid', + params: { uid: '@uid' }, + method: 'DELETE' + } + }) + ]) diff --git a/console/src/main/webapp/manager/app/services/logs.es6 b/console/src/main/webapp/manager/app/services/logs.es6 new file mode 100644 index 0000000000..c0406de733 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/logs.es6 @@ -0,0 +1,19 @@ +angular.module('manager') + .factory('Logs', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + 'admin_logs/:limit/:page', {}, { + query: { + method: 'GET', + cache: true, + isArray: true + } + }) + ]) + .factory('UserLogs', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + 'admin_logs/:id/:limit/:page', {}, { + query: { + method: 'GET', + cache: true, + isArray: true + } + }) + ]) diff --git a/console/src/main/webapp/manager/app/services/messages.es6 b/console/src/main/webapp/manager/app/services/messages.es6 new file mode 100644 index 0000000000..5ea65fea94 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/messages.es6 @@ -0,0 +1,30 @@ +angular.module('manager') + .factory('Templates', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + '../emailTemplates', {}, { + query: { + cache: true, + isArray: false + } + }) + ]).factory('Mail', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + '../:id/sendEmail', { id: '@id' }, { + save: { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + transformRequest: function (data) { + return $.param({ + subject: data.subject, + content: data.content, + attachments: data.attachments + }) + } + } + }) + ]).factory('Attachments', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + '../attachments', {}, { + query: { + cache: true, + isArray: false + } + }) + ]) diff --git a/console/src/main/webapp/manager/app/services/orgs.es6 b/console/src/main/webapp/manager/app/services/orgs.es6 new file mode 100644 index 0000000000..a7dd3a5d96 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/orgs.es6 @@ -0,0 +1,44 @@ +angular.module('manager') + .factory('Orgs', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + 'orgs/:id', {}, { + query: { + cache: true, + method: 'GET', + isArray: true + }, + get: { + params: { id: '@id' }, + method: 'GET', + cache: true, + isArray: false + }, + update: { + params: { id: '@id' }, + method: 'PUT' + }, + delete: { + params: { id: '@id' }, + method: 'DELETE' + } + }) + ]).factory('OrgsRequired', ['$resource', 'CONSOLE_PUBLIC_PATH', ($resource, baseUri) => + $resource(baseUri + 'orgs/requiredFields', {}, { + query: { + method: 'GET', + cache: true, + transformResponse: (data) => { + const response = {} + JSON.parse(data).forEach(key => { response[key] = true }) + return response + } + } + }) + ]).factory('OrgsType', ['$resource', 'CONSOLE_PUBLIC_PATH', ($resource, baseUri) => + $resource(baseUri + 'orgs/orgTypeValues', {}, { + query: { + method: 'GET', + cache: true, + isArray: true + } + }) + ]) diff --git a/console/src/main/webapp/manager/app/services/roles.es6 b/console/src/main/webapp/manager/app/services/roles.es6 new file mode 100644 index 0000000000..a5f7cb0840 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/roles.es6 @@ -0,0 +1,52 @@ +angular.module('manager') + .factory('Role', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + 'roles/:id', {}, { + query: { + cache: true, + method: 'GET', + isArray: true + }, + get: { + isArray: false + }, + update: { + params: { id: '@originalID' }, + method: 'PUT' + }, + delete: { + params: { id: '@cn' }, + method: 'DELETE' + } + }) + ]).factory('roleAdminList', [() => { + const adminRoles = [ + 'SUPERUSER', + 'ADMINISTRATOR', + 'GN_ADMIN', + 'GN_USERADMIN', + 'GN_EDITOR', + 'GN_REVIEWER', + 'ORGADMIN', + 'EXTRACTORAPP', + 'USER', + 'PENDING', + 'EXPIRED', + 'REFERENT', + 'TEMPORARY' + ] + return () => adminRoles + }]).factory('readonlyRoleList', [() => { + const readonlyRoles = [ + 'PENDING', + 'EXPIRED', + 'TEMPORARY', + 'ORGADMIN' + ] + return readonlyRoles + }]).factory( + 'expiredRole', () => 'EXPIRED' + ).factory( + 'temporaryRole', () => 'TEMPORARY' + ).factory('roleAdminFilter', ['roleAdminList', (roleAdminList) => + (role) => roleAdminList().indexOf(role.cn) >= 0 + ]) diff --git a/console/src/main/webapp/manager/app/services/roles_users.es6 b/console/src/main/webapp/manager/app/services/roles_users.es6 new file mode 100644 index 0000000000..600bbcb955 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/roles_users.es6 @@ -0,0 +1,4 @@ +angular.module('manager') + .factory('RolesUsers', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + 'roles_users', {}, {}) + ]) diff --git a/console/src/main/webapp/manager/app/services/translate.es6 b/console/src/main/webapp/manager/app/services/translate.es6 new file mode 100644 index 0000000000..9b3bbd1bc6 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/translate.es6 @@ -0,0 +1,11 @@ +angular.module('manager') + .factory('translate', ['$translate', ($translate) => (str, dict) => { + const promise = $translate(str) + if (dict) { + promise.then((v) => { + dict[str.split('.')[1]] = v + return v + }) + } + return promise + }]) diff --git a/console/src/main/webapp/manager/app/services/users.es6 b/console/src/main/webapp/manager/app/services/users.es6 new file mode 100644 index 0000000000..4eefe6f911 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/users.es6 @@ -0,0 +1,68 @@ +angular.module('manager') + .factory('User', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + 'users/:id', { id: '@uid' }, { + query: { + cache: true, + method: 'GET', + isArray: true + }, + get: { + cache: true + }, + update: { + params: { id: '@originalID' }, + method: 'PUT' + } + }) + ]).factory('Email', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + '../:id/emails', { id: '@id' }, { + query: { + method: 'GET', + isArray: false + } + }) + ]).factory('Profile', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + 'users/profile', {}, { + query: { + method: 'GET', + isArray: false + } + }) + ]).factory('UserRequired', ['$resource', 'CONSOLE_PUBLIC_PATH', ($resource, baseUri) => + $resource(baseUri + 'users/requiredFields', {}, { + get: { + method: 'GET', + cache: true, + transformResponse: (data) => { + const response = {} + JSON.parse(data).forEach(key => { response[key] = true }) + return response + }, + headers: { + 'Content-Type': 'application/json' + } + } + }) + ]).factory('ExportCSV', ['$http', 'CONSOLE_PRIVATE_PATH', ($http, baseUri) => { + return users => { + return $http.post(baseUri + 'export/users.csv', users, { + cache: false, + headers: { + 'Content-Type': 'application/json', + Accept: 'text/csv' + } + }) + } + } + ]).factory('ExportVCF', ['$http', 'CONSOLE_PRIVATE_PATH', ($http, baseUri) => { + return users => { + return $http.post(baseUri + 'export/users.vcf', users, { + cache: false, + headers: { + 'Content-Type': 'application/json', + Accept: 'text/x-vcard' + } + }) + } + } + ]) diff --git a/console/src/main/webapp/manager/app/services/util.es6 b/console/src/main/webapp/manager/app/services/util.es6 new file mode 100644 index 0000000000..0bde845785 --- /dev/null +++ b/console/src/main/webapp/manager/app/services/util.es6 @@ -0,0 +1,40 @@ +angular.module('manager') + .factory('date', () => { + const format = 'YYYY-MM-DD' + + return { + + getFromDiff: (interval) => { + let m = moment().add(1, 'day') + if (interval === 'day') { + m = m.subtract(1, 'day') + } + if (interval === 'week') { + m = m.subtract(1, 'weeks') + } + if (interval === 'month') { + m = m.subtract(1, 'months') + } + if (interval === '3month') { + m = m.subtract(3, 'months') + } + if (interval === 'year') { + m = m.subtract(1, 'year') + } + return m.format(format) + }, + + getDefault: () => moment().add(1, 'day').subtract(1, 'month').format(format), + + getEnd: () => moment().add(1, 'day').format(format) + + } + }) + .factory('PlatformInfos', ['$resource', 'CONSOLE_PRIVATE_PATH', ($resource, baseUri) => + $resource(baseUri + 'platform/infos', {}, { + query: { + method: 'GET', + isArray: false + } + }) + ]) diff --git a/console/src/main/webapp/manager/app/styles/app.less b/console/src/main/webapp/manager/app/styles/app.less new file mode 100644 index 0000000000..77bb40fa70 --- /dev/null +++ b/console/src/main/webapp/manager/app/styles/app.less @@ -0,0 +1,618 @@ +@lightblue: #009FE3; +@error: #a94442; +@border: #ddd; + +@tablet: 768px; +@large: 992px; + +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('./fonts/glyphicons-halflings-regular.eot'); + src: url('./fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('./fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('./fonts/glyphicons-halflings-regular.woff') format('woff'), url('./fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('./fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} + +a { + color : @lightblue; +} +.btn-primary { + background-color : @lightblue; +} +body { + padding-bottom: 2em; +} + +iframe { + width : 100%; + height : 90px; + border : none; + overflow : hidden; +} + +.profile { + font-size: 14px; + margin-top: 1em; + font-weight: normal; +} + +.orgs, +.roles, +browse { + input { + margin-bottom: 1em; + } + .create-btn { + display: flex; + .btn { + flex-grow : 1; + margin : 0 .5em; + } + } +} + +#newRoleName{ + text-transform:uppercase; +} + +.container > .nav { + margin-bottom: 2em; +} + +.col-md-10 { + margin-top: 1em; + @media (min-width: @large) { + margin-top: 0; + } +} + +section { + .nav { + background: @lightblue; + @media (min-width: @large) { + margin : 0 -15px; + border : none; + } + border : 1px solid rgb(221, 221, 221); + border-radius : 5px; + padding : 1em 0; + li a { + border-radius : 0; + color : #fff; + } + li a:hover, + li.active a:hover, + li.active a { + background : @lightblue; + font-weight : bold; + } + } + .roles-title { + margin-top : 0; + padding-top : .1em; + margin-bottom : 1em; + } +} + +.pagination { + margin-top: 5px; +} +.pagination > li > a { + cursor : pointer; + padding : 6px 9px; +} +.pagination > .active > a { + background: @lightblue; +} +.panel-filter-table-reset { + position: absolute; + right: 2.5em; + top: 1.9em; + &:hover { + cursor: pointer; + } +} + +// ANALYTICS +.analytics { + hr { + margin-top: 2em; + } + .stats-conf { + margin: -.45em 0 0 1em; + } + .chosen-container { + float: right + } + .chosen-single { + margin-top : -.45em; + } +} + +// USER +.user { + .password { + height: 2.4em; + } + .chosen-container { + width: 100% !important; + } + .ql-toolbar { + border-radius: 4px 4px 0 0; + } + .ql-container { + border-radius: 0 0 4px 4px; + } +} + +// MESSAGES +.user-messages { + select { + margin-bottom : 0.25em; + margin-top : -0.25em; + } +} + +.stats { + .pull-right { + margin-top : -.75em; + } + table { + margin-top : 1em; + } +} + +// HOME +.home { + .alerts { + .alert { + margin-bottom : 0; + border-radius : 0; + .glyphicon, + a { + color: white; + } + .manage { + font-weight : bold; + } + .glyphicon { + font-size : 2.7em; + &:first-of-type { + font-size : 2.3em; + margin-left : -.5em; + } + } + &.alert-info { + background-color : @lightblue; + border-color : @lightblue; + padding : 30px 15px; + font-weight : bold; + a { + font-size: 1.2em; + } + } + &.alert-danger { + background-color : #E40615; + border-color : #E40615; + } + &.alert-warning { + background-color : #F29200; + border-color : #F29200; + } + &:first-child { + border-top-left-radius : 4px; + border-top-right-radius : 4px; + } + &:last-child { + border-bottom-left-radius : 4px; + border-bottom-right-radius : 4px; + } + } + } + .jumbotron { + margin-top : 2em; + padding : 24px; + h1 { + font-size : 48px; + } + h1, p { + text-align : center; + } + p { + font-size: 17px; + } + dir-pagination-controls { + display : block; + text-align : center; + } + table { + table-layout : fixed; + th, td { + width : 40%; + overflow : hidden; + text-overflow : ellipsis; + white-space : nowrap; + &:first-of-type { + width : 20%; + } + } + } + } +} + +// ASIDE +.aside { + @media (min-width: @large) { + border-right: 2px solid @lightblue; + } + .alert-info { + background-color : @lightblue; + border-color : @lightblue; + a:not(.btn) { + color : white; + } + } + .category { + display: block; + padding: .5em 0; + &.active { + font-weight: bold; + } + } +} + +.filter-table { + width: 25%; +} +.filter-table-reset { + position : absolute; + right : 1.5em; + top : 0.7em; + &:hover { + cursor: pointer; + } +} +.pending { + font-style: italic; +} + +.background { + position : absolute; + top : 0; + bottom : 0; + left : 0; + right : 0; + display : flex; + align-items : center; + justify-content : center; + background : rgba(255,255,255,.65); + z-index : 100; + .panel { + width : 40em; + box-shadow : 0 0 5em rgba(0,0,0,.4); + } + .panel-body { + min-height : 8em; + } + .close { + margin-top : -.4em; + margin-right : -.4em; + } +} + +.users { + @media (min-width: @large) { + margin: 0 -15px; + } + em { + color: #888; + } + .users-selection { + display: flex; + justify-content: center; + > .btn-primary, + > .dropdown-toggle { + padding: 6px; + } + } + .users-checkboxes { + text-align: center; + } + .table thead th { + vertical-align: middle; + } +} + +// STATS +@media (min-width: @large) { + .chosen-container { + width: 16em !important; + } + .stats-conf { + float: right; + } +} +.stats { + .progress { + width : 10em; + margin : 5em auto; + height : 2em; + } + .progress-bar { + border-radius : 4px; + background-color : @lightblue; + } + .alert { + margin-top : 2em; + } +} + +// AREA +.area { + position: relative; + .loading { + position : absolute; + top : -15px; + left : -15px; + right : -15px; + bottom : -15px; + background : rgba(0,0,0,.3); + z-index : 1000; + text-align : center; + font-size : 200%; + color : #fff; + line-height : 10em; + } + .map { + height : 650px; + width : ~"calc(100% + 60px)"; + position : relative; + border-radius : 5px 0 0 5px; + margin : -15px 0 -15px -30px; + border : none; + border-right: 1px solid @border; + } + .ol-zoom { + top : auto; + bottom : .5em; + } + .ol-dragbox { + border-color: @lightblue; + } + .ol-control button { + background: rgba(0, 139, 207, .5); + &:hover { + background: rgba(0, 159, 227, .8); + } + } + .alert-info { + text-align : center; + margin: 15px -30px 15px; + border-radius: 0 4px 0 0; + border-width: 0 0 1px 0; + } + @media (min-width: @large) { + .map { + width : ~"calc(100% + 30px)"; + } + .alert-info { + margin-top: -15px; + } + .importexport { + margin-top: 2em; + display: flex; + .btn { + flex: 1; + } + } + } + .attributions { + position : absolute; + bottom : 0; + right : 0; + z-index : 1; + background : rgba(255,255,255, 0.5); + padding : .25em 1em; + font-size : 11px; + text-shadow : 0 0 1px #FFF; + color : #666; + } + .selection { + position : absolute; + top : .5em; + right : .5em; + z-index : 1; + } + .search-container { + position : absolute; + top : .5em; + left : .5em; + z-index : 1; + width : 15em; + } + .search-clear { + position : absolute; + right : 0; + top : 0; + } + .pagination { + margin: 10px 0 0; + } + .alternate { + opacity: 75%; + font-style: italic; + } +} +.autocomplete-suggestions { + border-radius: 4px; + font-size: 13px; +} +.autocomplete-suggestion span { + display : inline-block; + width : 0.7em; + text-align : center; + margin-left : -0.5em; +} + +// DATE +date { + display : flex; + .input-group { + width : 16em; + margin-right : 1em; + } + select { + flex : 1; + } +} + +// LOGS +.logs { + .reset { + margin-top : -.75em; + } + .chosen-container { + width : 8em !important; + float : right + } + .chosen-drop { + width : 21em; + } + .custom { + width : 1em; + } + table { + th, td { + width: 27%; + &:first-of-type { + width: 19%; + } + select { + max-width: 50%; + } + .input-group { + max-width: 15em; + } + input { + padding : 3px; + } + } + select { + width : auto; + } + date { + select { + max-width : none; + height : 30px; + line-height : 30px; + padding : 5px 10px; + font-size : 12px; + line-height : 1.5; + border-radius : 3px; + } + } + } + .header-filter > span { + display : inline-block; + padding: .5em; + } +} + +.delete { + opacity: .5; +} + +.delegation_active { + color: #3c763d; +} +.delegation_inactive { + color: #8a6d3b; +} +.delegation_restricted { + margin-top: 4em; +} + +.user-form, +.org-form { + .required .col-sm-8:after { + position: absolute; + right: 0.35em; + top: -0.4em; + content: '*'; + color: @lightblue; + opacity: .8; + font-size: 200%; + line-height: 1.4em; + } +} + +button.delete-logo { + position: absolute; + background: white; + right: 20px; + top: 5px; + width: 24px; + opacity: .6; + border-radius: 2px; + &:hover { + opacity: 1; + } +} + +// CHOSEN +.chosen-container-multi .chosen-choices { + border-radius : 4px; + padding : 2px 5px; + li.search-choice { + background : #f5f5f5; + box-shadow : none; + } +} +.chosen-container { + * { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important; + } + .chosen-drop { + border-top : 1px solid #aaa; + border-radius : 4px; + top : calc(100% + 2px); + } + .chosen-single { + background : white; + padding : 4px 0 4px 8px; + height : 33px; + div b { + background-position: 0 6px; + } + } +} +.chosen-container-active.chosen-with-drop .chosen-single { + background : white; + div b { + background-position: -18px 6px; + } +} + +[flash-message] { + position : absolute; + width : 25em; + left : 50%; + margin-left : -12.5em; + top : 1em; + z-index : 2000; +} + +.glyphicon-spin { + animation: spin 2s infinite linear; +} +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(359deg); + } +} + +input.ng-invalid { + border-color: @error; +} diff --git a/console/src/main/webapp/manager/app/styles/svg.less b/console/src/main/webapp/manager/app/styles/svg.less new file mode 100644 index 0000000000..0d8bc4107f --- /dev/null +++ b/console/src/main/webapp/manager/app/styles/svg.less @@ -0,0 +1,32 @@ +@lightblue: #009FE3; +svg { + background: white; +} + +.chartist { + height: 25em; +} +.ct-label { + font-size: 1rem; +} +.ct-barlabel { + font-size: 1rem; + fill: white; +} +.ct-series-a +.ct-bar, +.ct-series-a +.ct-line, +.ct-series-a +.ct-point, +.ct-series-a +.ct-slice-donut +{ + stroke: @lightblue; +} + +text { + font-size : 11px; + font-family : Arial; + fill : #AAA; +} diff --git a/console/src/main/webapp/manager/app/templates/dirPagination.tpl.html b/console/src/main/webapp/manager/app/templates/dirPagination.tpl.html new file mode 100644 index 0000000000..1c7f88cf19 --- /dev/null +++ b/console/src/main/webapp/manager/app/templates/dirPagination.tpl.html @@ -0,0 +1,18 @@ + diff --git a/console/src/main/webapp/manager/app/templates/orgForm.tpl.html b/console/src/main/webapp/manager/app/templates/orgForm.tpl.html new file mode 100644 index 0000000000..27d8f464c2 --- /dev/null +++ b/console/src/main/webapp/manager/app/templates/orgForm.tpl.html @@ -0,0 +1,74 @@ +
+ +
pas de model
+ +
+ +
+ + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + + +
+ +
+ +
diff --git a/console/src/main/webapp/manager/app/templates/roleForm.tpl.html b/console/src/main/webapp/manager/app/templates/roleForm.tpl.html new file mode 100644 index 0000000000..c8f9a193aa --- /dev/null +++ b/console/src/main/webapp/manager/app/templates/roleForm.tpl.html @@ -0,0 +1,39 @@ +
+ +
pas de model
+ +
+ +
+ + +
+ +
+ +

role.helpFormat

+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
diff --git a/console/src/main/webapp/manager/app/templates/userForm.tpl.html b/console/src/main/webapp/manager/app/templates/userForm.tpl.html new file mode 100644 index 0000000000..8a3b8e8692 --- /dev/null +++ b/console/src/main/webapp/manager/app/templates/userForm.tpl.html @@ -0,0 +1,144 @@ +
+ +
pas de model
+ +
+ +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+ {{model.uid}} (change) +
+
+ + +
+
+
+ +
+ + + user.reset + + +
+
+
+ +
+ +
+
+
+
+
+ +
diff --git a/console/src/main/webapp/manager/brunch-config.js b/console/src/main/webapp/manager/brunch-config.js new file mode 100644 index 0000000000..e2e2d21365 --- /dev/null +++ b/console/src/main/webapp/manager/brunch-config.js @@ -0,0 +1,73 @@ + module.exports = { + config: { + plugins: { + ng_templates: { + module: 'manager', + relativePath: 'app/' + }, + babel: { + pattern: /\.es6/, + presets: ['@babel/env'] + } + }, + files: { + javascripts: { + joinTo: { + 'app.js': /^app/, + 'libraries.js': [ + 'vendor/jquery.js', + 'vendor/angular.js', + 'vendor/angular-resource.js', + 'vendor/angular-sanitize.js', + 'vendor/auto-complete.min.js', + 'vendor/bootstrap-datepicker.js', + 'vendor/bootstrap.min.js', + 'vendor/router.es5.js', + 'vendor/chosen.jquery.js', + 'vendor/angular-chosen.js', + 'vendor/angular-flash.js', + 'vendor/angular-translate.js', + 'vendor/angular-translate-loader-static-files.js', + 'vendor/dirPagination.js', + 'vendor/chartist.js', + 'vendor/inline.js', + 'vendor/select2.full.js', + 'vendor/moment.min.js', + 'vendor/quill.js', + 'vendor/saveSvgAsPng.js', + 'vendor/ol.js', + 'vendor/FileSaver.js', + 'vendor/promise.min.js', + 'vendor/fetch.js', + 'vendor/moment-locale-de.js', + 'vendor/moment-locale-fr.js', + 'vendor/moment-locale-es.js' + ] + } + }, + stylesheets: { + joinTo: { + 'app.css': /^app/, + 'libraries.css': [ + 'vendor/auto-complete.css', + 'vendor/bootstrap-datepicker3.css', + 'vendor/bootstrap.css', + 'vendor/chosen.min.css', + 'vendor/ol.css', + 'vendor/quill.snow.css', + 'vendor/select2.css' + ], + 'svg.css': [ + 'vendor/chartist.min.css', + 'app/styles/svg.scss' + ] + } + }, + templates: { + joinTo: { + 'templates.js': [/^app/] + } + } + } + } + }; diff --git a/console/src/main/webapp/manager/package-lock.json b/console/src/main/webapp/manager/package-lock.json new file mode 100644 index 0000000000..6dc19bf8d0 --- /dev/null +++ b/console/src/main/webapp/manager/package-lock.json @@ -0,0 +1,7040 @@ +{ + "name": "manager", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/core": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz", + "integrity": "sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.0", + "@babel/helper-module-transforms": "^7.11.0", + "@babel/helpers": "^7.10.4", + "@babel/parser": "^7.11.1", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.11.0", + "@babel/types": "^7.11.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + } + }, + "@babel/generator": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", + "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", + "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", + "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.10.5", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", + "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-regex": "^7.10.4", + "regexpu-core": "^4.7.0" + } + }, + "@babel/helper-define-map": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", + "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", + "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", + "dev": true, + "requires": { + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", + "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", + "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-module-transforms": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", + "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/template": "^7.10.4", + "@babel/types": "^7.11.0", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", + "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", + "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-replace-supers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", + "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-simple-access": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", + "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz", + "integrity": "sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", + "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helpers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", + "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.2.tgz", + "integrity": "sha512-Vuj/+7vLo6l1Vi7uuO+1ngCDNeVmNbTngcJFKCR/oEtz8tKz0CJxZEGmPt9KcIloZhOZ3Zit6xbpXT2MDlS9Vw==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", + "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.4.tgz", + "integrity": "sha512-EcuXeV4Hv1X3+Q1TsuOmyyxeTRiSqurGJ26+I/FW1WbymmRRapVORm6x1Zl3iDIHyRxEs+VXWp6qnlcfcJSbbw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.7.4", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", + "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", + "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.7.tgz", + "integrity": "sha512-3qp9I8lelgzNedI3hrhkvhaEYree6+WHnyA/q4Dza9z7iEIs1eyhWyJnetk3jJ69RT0AT4G0UhEGwyGFJ7GUuQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.7.4" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", + "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", + "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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==", + "dev": true, + "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==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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==", + "dev": true, + "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==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", + "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", + "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", + "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", + "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz", + "integrity": "sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", + "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-define-map": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", + "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz", + "integrity": "sha512-4jFMXI1Cu2aXbcXXl8Lr6YubCn6Oc7k9lLsu8v61TZh+1jny2BWmdtvY9zSUlLdGUvcy9DMAWyZEOqjsbeg/wA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", + "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", + "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", + "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", + "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", + "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", + "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", + "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", + "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", + "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", + "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.10.4", + "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", + "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", + "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", + "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", + "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", + "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", + "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", + "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", + "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", + "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz", + "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", + "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-regex": "^7.10.4" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", + "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", + "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", + "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/preset-env": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.7.tgz", + "integrity": "sha512-pCu0hrSSDVI7kCVUOdcMNQEbOPJ52E+LrQ14sN8uL2ALfSqePZQlKrOy+tM4uhEdYlCHi4imr8Zz2cZe9oSdIg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.7.4", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-async-generator-functions": "^7.7.4", + "@babel/plugin-proposal-dynamic-import": "^7.7.4", + "@babel/plugin-proposal-json-strings": "^7.7.4", + "@babel/plugin-proposal-object-rest-spread": "^7.7.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.7.4", + "@babel/plugin-proposal-unicode-property-regex": "^7.7.7", + "@babel/plugin-syntax-async-generators": "^7.7.4", + "@babel/plugin-syntax-dynamic-import": "^7.7.4", + "@babel/plugin-syntax-json-strings": "^7.7.4", + "@babel/plugin-syntax-object-rest-spread": "^7.7.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.7.4", + "@babel/plugin-syntax-top-level-await": "^7.7.4", + "@babel/plugin-transform-arrow-functions": "^7.7.4", + "@babel/plugin-transform-async-to-generator": "^7.7.4", + "@babel/plugin-transform-block-scoped-functions": "^7.7.4", + "@babel/plugin-transform-block-scoping": "^7.7.4", + "@babel/plugin-transform-classes": "^7.7.4", + "@babel/plugin-transform-computed-properties": "^7.7.4", + "@babel/plugin-transform-destructuring": "^7.7.4", + "@babel/plugin-transform-dotall-regex": "^7.7.7", + "@babel/plugin-transform-duplicate-keys": "^7.7.4", + "@babel/plugin-transform-exponentiation-operator": "^7.7.4", + "@babel/plugin-transform-for-of": "^7.7.4", + "@babel/plugin-transform-function-name": "^7.7.4", + "@babel/plugin-transform-literals": "^7.7.4", + "@babel/plugin-transform-member-expression-literals": "^7.7.4", + "@babel/plugin-transform-modules-amd": "^7.7.5", + "@babel/plugin-transform-modules-commonjs": "^7.7.5", + "@babel/plugin-transform-modules-systemjs": "^7.7.4", + "@babel/plugin-transform-modules-umd": "^7.7.4", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.7.4", + "@babel/plugin-transform-new-target": "^7.7.4", + "@babel/plugin-transform-object-super": "^7.7.4", + "@babel/plugin-transform-parameters": "^7.7.7", + "@babel/plugin-transform-property-literals": "^7.7.4", + "@babel/plugin-transform-regenerator": "^7.7.5", + "@babel/plugin-transform-reserved-words": "^7.7.4", + "@babel/plugin-transform-shorthand-properties": "^7.7.4", + "@babel/plugin-transform-spread": "^7.7.4", + "@babel/plugin-transform-sticky-regex": "^7.7.4", + "@babel/plugin-transform-template-literals": "^7.7.4", + "@babel/plugin-transform-typeof-symbol": "^7.7.4", + "@babel/plugin-transform-unicode-regex": "^7.7.4", + "@babel/types": "^7.7.4", + "browserslist": "^4.6.0", + "core-js-compat": "^3.6.0", + "invariant": "^2.2.2", + "js-levenshtein": "^1.1.3", + "semver": "^5.5.0" + } + }, + "@babel/runtime": { + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", + "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", + "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.0", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.11.0", + "@babel/types": "^7.11.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", + "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@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 + }, + "acorn": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "dev": true + }, + "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 + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "optional": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.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" + } + } + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "angular": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/angular/-/angular-1.8.0.tgz", + "integrity": "sha512-VdaMx+Qk0Skla7B5gw77a8hzlcOakwF8mjlW13DpIWIDlfqwAbSSLfd8N/qZnzEmQF4jC4iofInd3gE7vL8ZZg==", + "dev": true + }, + "angular-chosen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/angular-chosen/-/angular-chosen-1.0.8.tgz", + "integrity": "sha1-2o89zo2z8Ra2dLdjKnHL0rzxHfI=", + "dev": true + }, + "angular-chosen-localytics": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/angular-chosen-localytics/-/angular-chosen-localytics-1.9.3.tgz", + "integrity": "sha512-ruvcI8gxgeHXbycZ5rW/tkPeCy0uQfsEuGts9ectmV6Bob/bIMthWbx3tf4DUMKF0Ed7hUf2ipwGrS/ZphXB3Q==", + "dev": true, + "requires": { + "angular": "^1.5.7", + "chosen-js": "^1.6.1", + "jquery": "^3.0.0" + } + }, + "angular-flash-alert": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/angular-flash-alert/-/angular-flash-alert-1.1.1.tgz", + "integrity": "sha1-AURd6BvDlLi53ugM12l5I9lEpeY=", + "dev": true + }, + "angular-new-router": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/angular-new-router/-/angular-new-router-0.5.3.tgz", + "integrity": "sha1-eeXuwmdLC4XNfzaRVxa6M6dzSrg=", + "dev": true, + "requires": { + "requirejs": "^2.1.15", + "route-recognizer": "git://github.com/btford/route-recognizer.git", + "traceur": "^0.0.72" + } + }, + "angular-resource": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/angular-resource/-/angular-resource-1.8.0.tgz", + "integrity": "sha512-9woUq3kDwoT7R6SjKX8vaJMhOplYBm9sqRAxKgDhDIdPyA8iBowqQIusf9+8Q+z/HlXb8ZXvKspJyKXrxmKdvg==", + "dev": true + }, + "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==", + "dev": true + }, + "angular-templates-brunch-next": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/angular-templates-brunch-next/-/angular-templates-brunch-next-0.0.7.tgz", + "integrity": "sha1-vS+Jd1cdbwumQEwxQ+xacCacCGU=", + "dev": true, + "requires": { + "coffee-script": "~1.9.2" + } + }, + "angular-translate": { + "version": "2.18.3", + "resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.3.tgz", + "integrity": "sha512-ziEi1nTaNIsdn3iKpcALaSFjM9CjwvoIWE/EKepajB/6qT5oB3K1IU5VcaH7Bd0scNRfaJpW+qUD6nsHWDEZ6A==", + "dev": true, + "requires": { + "angular": "^1.8.0" + } + }, + "angular-translate-loader-static-files": { + "version": "2.18.3", + "resolved": "https://registry.npmjs.org/angular-translate-loader-static-files/-/angular-translate-loader-static-files-2.18.3.tgz", + "integrity": "sha512-B5hD7aWjFr2TZ6YTnmoTFSscI+eDOXSi3GksyTvtqs4cPZI2lgnO38/Pm1gfN1wpUQcs2Rm/V+z8n58TERZyBQ==", + "dev": true, + "requires": { + "angular-translate": "~2.18.3" + } + }, + "angular-utils-pagination": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/angular-utils-pagination/-/angular-utils-pagination-0.11.1.tgz", + "integrity": "sha1-7618iHm+swrT13cH+T49DvUfLGY=", + "dev": true + }, + "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-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "anysort": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anysort/-/anysort-2.0.0.tgz", + "integrity": "sha512-Vo6WEVULAOb5LraoA+6STmR5kr4CsCL4w3Jt8WMdSDH3N9LLpzjIcLnFE2cXZ4v50reshxM4DO/Y16ZHphHxWw==", + "dev": true, + "requires": { + "anymatch": "^3" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "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=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "optional": 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==", + "dev": true, + "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==", + "dev": true + } + } + }, + "assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "dev": true, + "requires": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true, + "optional": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.5.7.tgz", + "integrity": "sha1-9sqpwht3pcYgGxVwos3WM2Pj0xQ=", + "dev": true, + "requires": { + "depd": "~1.0.0" + } + }, + "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": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true, + "optional": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "array-filter": "^1.0.0" + } + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", + "dev": true, + "optional": true + }, + "babel-brunch": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-brunch/-/babel-brunch-7.0.1.tgz", + "integrity": "sha512-OVbEsiD4Y6eceuP8jrntCzVE647dGhSZq+bsXVB2uFYV5yI5BCY9jhAY4QR057we4NZgc98xna7Vv/JzRzwZuA==", + "dev": true, + "requires": { + "@babel/core": "^7.0.0", + "@babel/preset-env": "^7.0.0", + "anymatch": "^2.0.0", + "loggy": "^1.0.3" + } + }, + "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-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==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "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==", + "dev": true + }, + "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, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "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==", + "dev": true + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, + "bn.js": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==", + "dev": true + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "optional": true, + "requires": { + "hoek": "2.x.x" + } + }, + "bootstrap": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", + "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==", + "dev": true + }, + "bootstrap-datepicker": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/bootstrap-datepicker/-/bootstrap-datepicker-1.9.0.tgz", + "integrity": "sha512-9rYYbaVOheGYxjOr/+bJCmRPihfy+LkLSg4fIFMT9Od8WwWB/MB50w0JO1eBgKUMbb7PFHQD5uAfI3ArAxZRXA==", + "dev": true, + "requires": { + "jquery": ">=1.7.1 <4.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "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" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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=", + "dev": true, + "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==", + "dev": true + } + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "browserslist": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz", + "integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001111", + "electron-to-chromium": "^1.3.523", + "escalade": "^3.0.2", + "node-releases": "^1.1.60" + } + }, + "brunch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/brunch/-/brunch-3.0.0.tgz", + "integrity": "sha512-CcqjXFFRcHLm/JlLhTT6iO5JUDpeWuSkdtOHfDKKUNQ3rMDZQeGEa1DgZCOEpPpyhCECQDJcSdkkTqd4Q9zOOg==", + "dev": true, + "requires": { + "anymatch": "^3.1.1", + "anysort": "^2.0.0", + "brunch-skeletons": "^0.2.0", + "chokidar": "^3.3.1", + "commander": "~4.0.1", + "commonjs-require-definition": "~0.6.2", + "debug": "^4.1.1", + "deppack": "^0.9.2", + "deps-install": "~0.2.1", + "fcache": "~0.3", + "hosted-git-info": "^3.0.2", + "loggy": "~1.0.8", + "ncp": "^2.0.0", + "normalize-git-url": "^3.0.2", + "serve-handler": "^6.1.2", + "since-app-start": "~0.3.3", + "skemata": "~0.1.2", + "source-map": "~0.5", + "universal-path": "^0.1.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "commander": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.0.1.tgz", + "integrity": "sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } + } + }, + "brunch-skeletons": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/brunch-skeletons/-/brunch-skeletons-0.2.0.tgz", + "integrity": "sha512-+ywd1mCqyTBjx0ENEC918IhLnV2gaUMkAU49ioIrKcLdt9cT77OvpWDtSwuKcSo3BDhbGYo2Bx1c10qoEtd7Iw==", + "dev": true + }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "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=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001112", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001112.tgz", + "integrity": "sha512-J05RTQlqsatidif/38aN3PGULCLrg8OYQOlJUKbeYVzC2mGZkZLIztwRlB3MtrfLmawUmjFlNJvy/uhwniIe1Q==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true, + "optional": true + }, + "cbify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cbify/-/cbify-1.0.0.tgz", + "integrity": "sha1-GnrUBEsvkjF+ehut2jpydgeDXYQ=", + "dev": true, + "requires": { + "fn-args": "^1.0.0", + "wrappy": "^1.0.1" + } + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chartist": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/chartist/-/chartist-0.11.4.tgz", + "integrity": "sha512-H4AimxaUD738/u9Mq8t27J4lh6STsLi4BQHt65nOtpLk3xyrBPaLiLMrHw7/WV9CmsjGA02WihjuL5qpSagLYw==", + "dev": true + }, + "chokidar": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", + "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "dev": true, + "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" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "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==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "chosen-js": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/chosen-js/-/chosen-js-1.8.7.tgz", + "integrity": "sha512-eVdrZJ2U5ISdObkgsi0od5vIJdLwq1P1Xa/Vj/mgxkMZf14DlgobfB6nrlFi3kW4kkvKLsKk4NDqZj1MU1DCpw==", + "dev": true + }, + "chosen-npm": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/chosen-npm/-/chosen-npm-1.4.2.tgz", + "integrity": "sha1-7Ghq55ZRNHPbwXGxpjcC5tfhUV8=", + "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==", + "dev": true, + "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==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "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-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "cls": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/cls/-/cls-0.1.5.tgz", + "integrity": "sha1-3zIYz50UgHR/WE2IsZt0xrKBMXs=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "optional": true + }, + "coffee-script": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.9.3.tgz", + "integrity": "sha1-WW5ug/z8tnxZZKtw1ES+/wrASsc=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-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==", + "dev": true, + "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=", + "dev": true + }, + "colorette": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.1.0.tgz", + "integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==", + "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, + "optional": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commonjs-require-definition": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/commonjs-require-definition/-/commonjs-require-definition-0.6.3.tgz", + "integrity": "sha512-zBIo1YrBat66dXiTeXPNAsh8YfbTxwpOVP71bmjDhQciJoq8hv9eA9fG0fLDD/f9EZbsJkNq6V42Y+fdhaal4Q==", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "optional": true, + "requires": { + "boom": "2.x.x" + } + }, + "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==", + "dev": true, + "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" + } + }, + "css-brunch": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/css-brunch/-/css-brunch-2.10.0.tgz", + "integrity": "sha1-t8jnII6nqDfIYI6g6Oe5oDxKcvI=", + "dev": true, + "requires": { + "postcss": "~5.1.2", + "postcss-modules": "~0.5.0" + } + }, + "css-modules-loader-core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", + "integrity": "sha1-WQhmgpShvs0mGuCkziGwtVHyHRY=", + "dev": true, + "requires": { + "icss-replace-symbols": "1.1.0", + "postcss": "6.0.1", + "postcss-modules-extract-imports": "1.1.0", + "postcss-modules-local-by-default": "1.2.0", + "postcss-modules-scope": "1.1.0", + "postcss-modules-values": "1.3.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" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "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 + }, + "postcss": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.1.tgz", + "integrity": "sha1-AA29H47vIXqjaLmiEsX8QLKo8/I=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "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" + } + } + } + }, + "css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-assign": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-2.0.0.tgz", + "integrity": "sha1-6+BrHwfwja5ZdiDj3RYi83GhxXI=", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "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==", + "dev": true, + "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" + } + }, + "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 + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "deglob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-4.0.1.tgz", + "integrity": "sha512-/g+RDZ7yf2HvoW+E5Cy+K94YhgcFgr6C8LuHZD1O5HoNPkf3KY6RfXJ0DBGlB/NkLi5gml+G9zqRzk9S0mHZCg==", + "dev": true, + "requires": { + "find-root": "^1.0.0", + "glob": "^7.0.5", + "ignore": "^5.0.0", + "pkg-config": "^1.1.0", + "run-parallel": "^1.1.2", + "uniq": "^1.0.1" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "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, + "optional": true + }, + "depd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz", + "integrity": "sha1-gK7GTJ1tl+ZcwqnKqTwKpqv3Oqo=", + "dev": true + }, + "deppack": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/deppack/-/deppack-0.9.2.tgz", + "integrity": "sha512-Jo6XjXs9FsByEMigX668djDHZb2I1XCZkvZ8pxk6PBer1C4WX2nd86mDt0I0EcULqxDAEmyd4CgxKgMfi6kf8g==", + "dev": true, + "requires": { + "anymatch": "^3.0.0", + "async-each": "^1.0.3", + "browser-resolve": "^1.11.1", + "deep-assign": "^2.0.0", + "detective": "^5.2.0", + "glob": "^7.1.5", + "loggy": "^1.0.8", + "node-browser-modules": "^0.2.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } + } + }, + "deps-install": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/deps-install/-/deps-install-0.2.1.tgz", + "integrity": "sha512-jTTRh/r+pszOjEOioL/qTGCQkRBlqjxuhS7H2MjpSqZvpM10ZlJ005D04NZyq8UxsA82LxZX83k5wz6hzS6zYw==", + "dev": true, + "requires": { + "readdirp": "^3.2.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 + } + } + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dev": true, + "requires": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "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==", + "dev": true + } + } + }, + "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" + } + }, + "domain-browser": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-3.5.0.tgz", + "integrity": "sha512-zrzUu6auyZWRexjCEPJnfWc30Hupxh2lJZOJAF3qa2bCuD4O/55t0FvQt3ZMhEw++gjNkwdkOVZh8yA32w/Vfw==", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "electron-to-chromium": { + "version": "1.3.524", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.524.tgz", + "integrity": "sha512-ZUvklIBkfXQyA6IeiEss1nfKRICcdB5afAGZAaPGaExdfrkpUu/WWVO+X7QpNnphaVMllXnAcvKnVPdyM+DCPQ==", + "dev": true + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "dev": true, + "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==", + "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 + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "optional": true, + "requires": { + "prr": "~1.0.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==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "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==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", + "dev": true + }, + "escalade": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", + "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==", + "dev": true + }, + "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=", + "dev": true + }, + "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": { + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-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 + }, + "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" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "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-config-standard": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", + "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", + "dev": true + }, + "eslint-config-standard-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.1.0.tgz", + "integrity": "sha512-ULVC8qH8qCqbU792ZOO6DaiaZyHNS/5CZt3hKqHkEhVlhPEPN3nfBqqxJCyp59XrjIBZPu1chMYe9T2DXZ7TMw==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-es": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz", + "integrity": "sha512-f6fceVtg27BR02EYnBhgWLFQfK6bN4Ll0nQFrBHOlCsAyxeZkn0NHns5O0YZOPrV1B3ramd6cgFwaoFLcSkwEQ==", + "dev": true, + "requires": { + "eslint-utils": "^1.4.2", + "regexpp": "^3.0.0" + }, + "dependencies": { + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.18.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", + "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.11.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-node": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz", + "integrity": "sha512-1CSyM/QCjs6PXaT18+zuAXsjXGIGo5Rw630rSKwokSs2jrYURQc4R5JZpoanNCqwNmepg+0eZ9L7YiRUJb8jiQ==", + "dev": true, + "requires": { + "eslint-plugin-es": "^2.0.0", + "eslint-utils": "^1.4.2", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-plugin-react": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", + "integrity": "sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.1.0", + "object.entries": "^1.1.0", + "object.fromentries": "^2.0.0", + "object.values": "^1.1.0", + "prop-types": "^15.7.2", + "resolve": "^1.10.1" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "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" + } + }, + "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.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "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" + } + }, + "esprima-fb": { + "version": "7001.1.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-7001.1.0-dev-harmony-fb.tgz", + "integrity": "sha1-kH4gkZV1pmfdG0IzXeAM9mRtd7M=", + "dev": true + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "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==", + "dev": true, + "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==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=", + "dev": true + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "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" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "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" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "optional": 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==", + "dev": true + }, + "fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", + "dev": true + }, + "fast-url-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", + "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=", + "dev": true, + "requires": { + "punycode": "^1.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "fcache": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/fcache/-/fcache-0.3.0.tgz", + "integrity": "sha1-1F8vkIZCuRt5jogZXsR4gaUcPUQ=", + "dev": true + }, + "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-saverjs": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/file-saverjs/-/file-saverjs-1.3.6.tgz", + "integrity": "sha1-t6RU4Sb1bPcfy3AVm4NWFCZU544=", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "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" + } + } + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "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" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "fn-args": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fn-args/-/fn-args-1.0.0.tgz", + "integrity": "sha1-l02voa6sSsfCH6Ccw7gPZQEG7TI=", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "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, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "optional": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs-mode": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-mode/-/fs-mode-1.0.1.tgz", + "integrity": "sha1-cxAvQKoaJSId2g6qkGYW1toIJVo=", + "dev": true, + "requires": { + "cbify": "^1.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "generic-names": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-1.0.3.tgz", + "integrity": "sha1-LXhqEhruUIh2eWk56OO/+DbCCRc=", + "dev": true, + "requires": { + "loader-utils": "^0.2.16" + } + }, + "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==", + "dev": true + }, + "get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^2.0.1", + "once": "^1.3.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==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "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==", + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "dev": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "dev": true, + "optional": true, + "requires": { + "ajv": "^4.9.1", + "har-schema": "^1.0.5" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "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=", + "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==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "optional": true, + "requires": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true, + "optional": true + }, + "hosted-git-info": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.5.tgz", + "integrity": "sha512-i4dpK6xj9BIpVOTboXIlKG9+8HMKggcrMX7WA24xZtKwX0TPelq/rbaS5rCKeNX8sJXZJGdSxpnEGtta+wismQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^0.2.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=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "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 + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": 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" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "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": "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 + }, + "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" + } + } + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-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==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "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 + }, + "is-generator-function": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", + "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-nan": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz", + "integrity": "sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "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-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "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==", + "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==", + "dev": true, + "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==", + "dev": true, + "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, + "optional": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true, + "optional": true + }, + "javascript-brunch": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/javascript-brunch/-/javascript-brunch-2.10.0.tgz", + "integrity": "sha1-gmkgA1tDaPFKZ4JU0V+D4vQQEXI=", + "dev": true, + "requires": { + "esprima": "~3.0.0" + }, + "dependencies": { + "esprima": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz", + "integrity": "sha1-U88kes2ncxPlUcOqLnM0LT+099k=", + "dev": true + } + } + }, + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==", + "dev": true + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "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==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true, + "optional": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "optional": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "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, + "optional": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "jsx-ast-utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", + "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "object.assign": "^4.1.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "less": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", + "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", + "dev": true, + "requires": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.2.11", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "2.81.0", + "source-map": "^0.5.3" + } + }, + "less-brunch": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/less-brunch/-/less-brunch-2.10.0.tgz", + "integrity": "sha1-ewd4TaIwH1CRGX/4b4MQEQJMAyg=", + "dev": true, + "requires": { + "less": "~2.7.1", + "postcss": "~5.0.19", + "postcss-modules": "~0.4.0", + "progeny": "~0.5.0" + }, + "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 + }, + "postcss": { + "version": "5.0.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.0.21.tgz", + "integrity": "sha1-1M9vGXdGSMSSrFfCmPavs8BMrv4=", + "dev": true, + "requires": { + "js-base64": "^2.1.9", + "source-map": "^0.5.5", + "supports-color": "^3.1.2" + } + }, + "postcss-modules": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-0.4.1.tgz", + "integrity": "sha1-7aoYQvXK5T84EZy7LSAeAQExveQ=", + "dev": true, + "requires": { + "css-modules-loader-core": "^1.0.0", + "postcss": "^5.0.14", + "string-hash": "^1.1.0" + } + }, + "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" + } + } + } + }, + "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" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + }, + "dependencies": { + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "dev": true + }, + "loggy": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/loggy/-/loggy-1.0.8.tgz", + "integrity": "sha512-3gXR0rOS4fnxVXGeiQID95/IML//jSbKx5T1GatifpzpOwDoOsTtlt25gosiBq27Mn4YnbzWB/DsKncnaLWKSg==", + "dev": true, + "requires": { + "colorette": "~1.1", + "native-notifier": "~0.1.6" + } + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "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==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "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" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "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==", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "requires": { + "mime-db": "~1.33.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "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==", + "dev": true + }, + "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=", + "dev": true + }, + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "dev": true, + "requires": { + "brace-expansion": "^1.0.0" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", + "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "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 + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "native-notifier": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/native-notifier/-/native-notifier-0.1.6.tgz", + "integrity": "sha512-BwRtjqhNsS2HpeVrNmERzQqZTFxR+MoJr/RdfCwZ0ODKyc/0EcHQhwnPwZr99gT1uHgRqbDk3C5fOo70TVYlcQ==", + "dev": true, + "requires": { + "tag-shell": "~0.1.0" + } + }, + "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 + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-browser-modules": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-browser-modules/-/node-browser-modules-0.2.1.tgz", + "integrity": "sha512-Fk/3aTptZuJ7XS5pY4Xk6H8ZvygyUkj84nuUz2VzyYkAN+p4SiO5OsWg8J2iWqkwb01PO7W523dMA8R3nQqQyQ==", + "dev": true, + "requires": { + "assert": "^2.0.0", + "buffer": "^5.4.3", + "crypto-browserify": "^3.12.0", + "domain-browser": "^3.5.0", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.0", + "process": "^0.11.10", + "punycode": "^2.1.1", + "querystring-es3": "^0.2.1", + "readable-stream": "^3.4.0", + "stream-browserify": "^2.0.2", + "stream-http": "^3.1.0", + "string_decoder": "^1.3.0", + "timers-browserify": "^2.0.11", + "tty-browserify": "^0.0.1", + "url": "^0.11.0", + "util": "^0.12.1", + "vm-browserify": "^1.1.2" + } + }, + "node-releases": { + "version": "1.1.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", + "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==", + "dev": true + }, + "normalize-git-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-git-url/-/normalize-git-url-3.0.2.tgz", + "integrity": "sha1-jl8Uvgva7bc+ByADEKpBbCc1D8Q=", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "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 + } + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", + "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", + "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.1.tgz", + "integrity": "sha512-ZpZpjcJeugQfWsfyQlshVoowIIQ1qBGSVll4rfDq6JJVO//fesjoX808hXWfBjY+ROZgpKDI5TRSRBSoJiZ8eg==", + "dev": true, + "requires": { + "mimic-fn": "^2.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" + }, + "dependencies": { + "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 + } + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "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-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "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=", + "dev": true, + "requires": { + "p-limit": "^1.1.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=", + "dev": true + }, + "parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==", + "dev": true + }, + "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" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "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-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" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "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 + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-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=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "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==", + "dev": true + }, + "path-to-regexp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", + "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "dev": true, + "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": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "dev": true, + "optional": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pkg-conf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", + "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "load-json-file": "^5.2.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": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.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" + } + }, + "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 + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "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 + } + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "dev": true, + "requires": { + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.1.2.tgz", + "integrity": "sha1-vYSIama8rUia+vfGc+7V72OVUeI=", + "dev": true, + "requires": { + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "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" + } + } + } + }, + "postcss-modules": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-0.5.2.tgz", + "integrity": "sha1-nWgv7T8oK9ZLKqT+tvIqKvQ1/9o=", + "dev": true, + "requires": { + "css-modules-loader-core": "^1.0.1", + "generic-names": "^1.0.1", + "postcss": "^5.1.2", + "string-hash": "^1.1.0" + } + }, + "postcss-modules-extract-imports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", + "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true + } + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "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==", + "dev": true, + "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==", + "dev": true + } + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "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==", + "dev": true, + "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==", + "dev": true + } + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "requires": { + "icss-replace-symbols": "^1.1.0", + "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==", + "dev": true, + "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==", + "dev": true + } + } + }, + "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 + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progeny": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/progeny/-/progeny-0.5.2.tgz", + "integrity": "sha1-H+8czLprBX80TngNv3vcjFE1Ehs=", + "dev": true, + "requires": { + "async-each": "~0.1.4", + "fs-mode": "^1.0.1" + }, + "dependencies": { + "async-each": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-0.1.6.tgz", + "integrity": "sha1-tn6Z7c3fllQeRK9WKQzX1cbnBDk=", + "dev": true + } + } + }, + "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==", + "dev": true, + "optional": true, + "requires": { + "asap": "~2.0.3" + } + }, + "promise-polyfill": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", + "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true, + "optional": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "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==", + "dev": true + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true, + "optional": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "dev": true, + "requires": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "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==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "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==", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "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" + } + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "recast": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.8.8.tgz", + "integrity": "sha1-4U6NrYOwLY94J9l7Hz4mTtCNQhw=", + "dev": true, + "requires": { + "ast-types": "~0.5.7", + "cls": "~0.1.3", + "depd": "~1.0.0", + "esprima-fb": "~7001.1.0-dev-harmony-fb", + "private": "~0.1.5", + "source-map": "0.1.32" + }, + "dependencies": { + "source-map": { + "version": "0.1.32", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz", + "integrity": "sha1-yLbBZ3l7pHQKjqMyUhYv8IWRsmY=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "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==", + "dev": true, + "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": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/regexpu/-/regexpu-0.3.0.tgz", + "integrity": "sha1-jt5LMAzaoSlcUC9qdgMW3qG/mSA=", + "dev": true, + "requires": { + "recast": "^0.8.0", + "regenerate": "^1.0.0", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.2" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } + } + }, + "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==", + "dev": true, + "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" + } + }, + "regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, + "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=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~4.2.1", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "performance-now": "^0.2.0", + "qs": "~6.4.0", + "safe-buffer": "^5.0.1", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.0" + } + }, + "requirejs": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", + "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "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 + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "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==", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "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" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "route-recognizer": { + "version": "git://github.com/btford/route-recognizer.git#24bde1b66fb8868f2b37c1d9d13ac70152198eaf", + "from": "git://github.com/btford/route-recognizer.git", + "dev": true + }, + "rsvp": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", + "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", + "dev": true + }, + "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 + }, + "rxjs": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", + "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", + "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==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "save-svg-as-png": { + "version": "1.4.17", + "resolved": "https://registry.npmjs.org/save-svg-as-png/-/save-svg-as-png-1.4.17.tgz", + "integrity": "sha512-7QDaqJsVhdFPwviCxkgHiGm9omeaMBe1VKbHySWU6oFB2LtnGCcYS13eVoslUgq6VZC6Tjq/HddBd1K6p2PGpA==", + "dev": true + }, + "select2": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/select2/-/select2-4.0.13.tgz", + "integrity": "sha512-1JeB87s6oN/TDxQQYCvS5EFoQyvV6eYMZZ0AeA4tdFDYWN3BAGZ8npr17UBFddU0lgAt3H0yjX3X6/ekOj1yjw==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "serve-handler": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", + "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "fast-url-parser": "1.1.3", + "mime-types": "2.1.18", + "minimatch": "3.0.4", + "path-is-inside": "1.0.2", + "path-to-regexp": "2.2.1", + "range-parser": "1.2.0" + }, + "dependencies": { + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "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" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "since-app-start": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/since-app-start/-/since-app-start-0.3.3.tgz", + "integrity": "sha512-CvVjdZvSgaUP4mMOLn8NZtYKFF2RYboEaTBKdj5F+Jm6SpCQ/kZgnMVMNHhOUgBzHusBe8aio0Gri8ci2utOXw==", + "dev": true, + "requires": { + "debug": "~3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "skemata": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/skemata/-/skemata-0.1.2.tgz", + "integrity": "sha1-88UhAptnvm4IJfDfhn7peRbeRTA=", + "dev": true, + "requires": { + "fast-levenshtein": "^1.1.3" + } + }, + "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" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "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" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "optional": true, + "requires": { + "hoek": "2.x.x" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.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 + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "optional": 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" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "standard": { + "version": "14.3.4", + "resolved": "https://registry.npmjs.org/standard/-/standard-14.3.4.tgz", + "integrity": "sha512-+lpOkFssMkljJ6eaILmqxHQ2n4csuEABmcubLTb9almFi1ElDzXb1819fjf/5ygSyePCq4kU2wMdb2fBfb9P9Q==", + "dev": true, + "requires": { + "eslint": "~6.8.0", + "eslint-config-standard": "14.1.1", + "eslint-config-standard-jsx": "8.1.0", + "eslint-plugin-import": "~2.18.0", + "eslint-plugin-node": "~10.0.0", + "eslint-plugin-promise": "~4.2.1", + "eslint-plugin-react": "~7.14.2", + "eslint-plugin-standard": "~4.0.0", + "standard-engine": "^12.0.0" + } + }, + "standard-engine": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-12.1.0.tgz", + "integrity": "sha512-DVJnWM1CGkag4ucFLGdiYWa5/kJURPONmMmk17p8FT5NE4UnPZB1vxWnXnRo2sPSL78pWJG8xEM+1Tu19z0deg==", + "dev": true, + "requires": { + "deglob": "^4.0.1", + "get-stdin": "^7.0.0", + "minimist": "^1.2.5", + "pkg-conf": "^3.1.0" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-http": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.1.tgz", + "integrity": "sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=", + "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" + }, + "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 + }, + "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" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "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==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "stringstream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "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" + }, + "dependencies": { + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-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 + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "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=", + "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==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.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" + } + } + } + }, + "tag-shell": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tag-shell/-/tag-shell-0.1.0.tgz", + "integrity": "sha1-50OBbmpugFrDc19BYuAWuXp9370=", + "dev": true + }, + "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 + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "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==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "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-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=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "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" + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "dev": true, + "optional": true, + "requires": { + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true, + "optional": true + } + } + }, + "traceur": { + "version": "0.0.72", + "resolved": "https://registry.npmjs.org/traceur/-/traceur-0.0.72.tgz", + "integrity": "sha1-Psta9cJZvw4L75AL6qVlpOKFc7k=", + "dev": true, + "requires": { + "commander": "2.x", + "glob": "4.x", + "regexpu": "0.3.0", + "rsvp": "^3.0.13", + "semver": "2.x" + }, + "dependencies": { + "semver": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-2.3.2.tgz", + "integrity": "sha1-uYSPJdbPNjMwc+ye+IVtQvEjPlI=", + "dev": true + } + } + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "requires": { + "glob": "^7.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "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, + "optional": 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, + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "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 + }, + "uglify-js": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.6.4.tgz", + "integrity": "sha1-ZeovswWck5RpLxX+2HwrNsFrmt8=", + "dev": true, + "requires": { + "async": "~0.2.6", + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + } + }, + "uglify-js-brunch": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/uglify-js-brunch/-/uglify-js-brunch-2.10.0.tgz", + "integrity": "sha1-YM0PtlKIegLOarzRWI3lXcw0bwU=", + "dev": true, + "requires": { + "uglify-js": "~2.6.1" + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true + }, + "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==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "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==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "universal-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/universal-path/-/universal-path-0.1.0.tgz", + "integrity": "sha1-D8okyTbqPSKCAT0UNxDAZoftBnc=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "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==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "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=", + "dev": true + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", + "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "optional": true + }, + "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 + }, + "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" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": 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==", + "dev": true + }, + "whatwg-fetch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-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==", + "dev": true, + "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" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "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 + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "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" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } +} diff --git a/console/src/main/webapp/manager/package.json b/console/src/main/webapp/manager/package.json new file mode 100644 index 0000000000..88914c1709 --- /dev/null +++ b/console/src/main/webapp/manager/package.json @@ -0,0 +1,87 @@ +{ + "name": "manager", + "version": "0.0.1", + "description": "admin_Console", + "homepage": "https://github.com/georchestra/georchestra", + "main": "index.js", + "repository": { + "type": "git", + "url": "git://github.com/georchestra/georchestra.git" + }, + "bugs": { + "url": "https://github.com/georchestra/georchestra/issues" + }, + "dependencies": { + "whatwg-fetch": "^2.0.4" + }, + "devDependencies": { + "@babel/core": "^7.7.7", + "@babel/plugin-proposal-class-properties": "7.7.4", + "@babel/plugin-proposal-object-rest-spread": "7.7.7", + "@babel/plugin-transform-destructuring": "7.7.4", + "@babel/preset-env": "7.7.7", + "angular": ">=1.8", + "angular-chosen": "1.0.8", + "angular-chosen-localytics": "^1.9.3", + "angular-flash-alert": "^1.1.1", + "angular-new-router": "0.5.3", + "angular-resource": ">=1.8", + "angular-sanitize": ">=1.8", + "angular-templates-brunch-next": "0.0.7", + "angular-translate": "^2.18.1", + "angular-translate-loader-static-files": "^2.18.1", + "angular-utils-pagination": "^0.11.1", + "babel-brunch": "^7.0.1", + "babel-eslint": "^10.0.3", + "bootstrap": "3.4.1", + "bootstrap-datepicker": "1.9.0", + "brunch": "^3.0.0", + "chartist": "^0.11.4", + "chosen-npm": "1.4.2", + "css-brunch": "^2.10.0", + "file-saverjs": "1.3.6", + "javascript-brunch": "2.10.0", + "jquery": "^3.4.1", + "less-brunch": "2.10.0", + "moment": "^2.24.0", + "promise-polyfill": "^8.1.3", + "quill": "^1.3.7", + "save-svg-as-png": "^1.4.14", + "select2": "^4.0.12", + "standard": "^14.3.1", + "uglify-js-brunch": "^2.10.0" + }, + "standard": { + "parser": "babel-eslint", + "globals": [ + "angular", + "$", + "Quill", + "ol", + "moment", + "Chartist", + "autoComplete", + "saveSvgAsPng", + "Blob" + ], + "ignore": [ + "vendor/", + "public/" + ] + }, + "babel": { + "presets": [ + "@babel/preset-env" + ], + "plugins": [ + "@babel/plugin-transform-destructuring", + "@babel/plugin-proposal-object-rest-spread", + "@babel/plugin-proposal-class-properties" + ] + }, + "scripts": { + "lint": "./node_modules/.bin/standard app/**/*.{js,es6}", + "serve": "./node_modules/.bin/brunch watch", + "postinstall": "./node_modules/.bin/standard app/**/*.{js,es6} && ./node_modules/.bin/brunch build" + } +} diff --git a/console/src/main/webapp/manager/vendor/FileSaver.js b/console/src/main/webapp/manager/vendor/FileSaver.js new file mode 120000 index 0000000000..4b821c5008 --- /dev/null +++ b/console/src/main/webapp/manager/vendor/FileSaver.js @@ -0,0 +1 @@ +../node_modules/file-saverjs/FileSaver.js \ No newline at end of file diff --git a/console/src/main/webapp/manager/vendor/angular-chosen.js b/console/src/main/webapp/manager/vendor/angular-chosen.js new file mode 120000 index 0000000000..58fde634cf --- /dev/null +++ b/console/src/main/webapp/manager/vendor/angular-chosen.js @@ -0,0 +1 @@ +../node_modules/angular-chosen-localytics/dist/angular-chosen.js \ No newline at end of file diff --git a/console/src/main/webapp/manager/vendor/angular-flash.js b/console/src/main/webapp/manager/vendor/angular-flash.js new file mode 120000 index 0000000000..e9d9b68206 --- /dev/null +++ b/console/src/main/webapp/manager/vendor/angular-flash.js @@ -0,0 +1 @@ +../node_modules/angular-flash-alert/dist/angular-flash.js \ No newline at end of file diff --git a/console/src/main/webapp/manager/vendor/angular-resource.js b/console/src/main/webapp/manager/vendor/angular-resource.js new file mode 120000 index 0000000000..0553594f0f --- /dev/null +++ b/console/src/main/webapp/manager/vendor/angular-resource.js @@ -0,0 +1 @@ +../node_modules/angular-resource/angular-resource.js \ No newline at end of file diff --git a/console/src/main/webapp/manager/vendor/angular-sanitize.js b/console/src/main/webapp/manager/vendor/angular-sanitize.js new file mode 120000 index 0000000000..5293ef54f3 --- /dev/null +++ b/console/src/main/webapp/manager/vendor/angular-sanitize.js @@ -0,0 +1 @@ +../node_modules/angular-sanitize/angular-sanitize.js \ No newline at end of file diff --git a/console/src/main/webapp/manager/vendor/angular-translate-loader-static-files.js b/console/src/main/webapp/manager/vendor/angular-translate-loader-static-files.js new file mode 120000 index 0000000000..8554713a01 --- /dev/null +++ b/console/src/main/webapp/manager/vendor/angular-translate-loader-static-files.js @@ -0,0 +1 @@ +../node_modules/angular-translate-loader-static-files/angular-translate-loader-static-files.js \ No newline at end of file diff --git a/console/src/main/webapp/manager/vendor/angular-translate.js b/console/src/main/webapp/manager/vendor/angular-translate.js new file mode 120000 index 0000000000..265d709660 --- /dev/null +++ b/console/src/main/webapp/manager/vendor/angular-translate.js @@ -0,0 +1 @@ +../node_modules/angular-translate/dist/angular-translate.js \ No newline at end of file diff --git a/console/src/main/webapp/manager/vendor/angular.js b/console/src/main/webapp/manager/vendor/angular.js new file mode 120000 index 0000000000..c0f07201d5 --- /dev/null +++ b/console/src/main/webapp/manager/vendor/angular.js @@ -0,0 +1 @@ +../node_modules/angular/angular.js \ No newline at end of file diff --git a/console/src/main/webapp/manager/vendor/auto-complete.css b/console/src/main/webapp/manager/vendor/auto-complete.css new file mode 100644 index 0000000000..4261b1d033 --- /dev/null +++ b/console/src/main/webapp/manager/vendor/auto-complete.css @@ -0,0 +1,9 @@ +.autocomplete-suggestions { + text-align: left; cursor: default; border: 1px solid #ccc; border-top: 0; background: #fff; box-shadow: -1px 1px 3px rgba(0,0,0,.1); + + /* core styles should not be changed */ + position: absolute; display: none; z-index: 9999; max-height: 254px; overflow: hidden; overflow-y: auto; box-sizing: border-box; +} +.autocomplete-suggestion { position: relative; padding: 0 .6em; line-height: 23px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 1.02em; color: #333; } +.autocomplete-suggestion b { font-weight: normal; color: #1f8dd6; } +.autocomplete-suggestion.selected { background: #f0f0f0; } diff --git a/console/src/main/webapp/manager/vendor/auto-complete.min.js b/console/src/main/webapp/manager/vendor/auto-complete.min.js new file mode 100644 index 0000000000..2d0f0373b2 --- /dev/null +++ b/console/src/main/webapp/manager/vendor/auto-complete.min.js @@ -0,0 +1,3 @@ +// JavaScript autoComplete v1.0.4 +// https://github.com/Pixabay/JavaScript-autoComplete +var autoComplete=function(){function e(e){function t(e,t){return e.classList?e.classList.contains(t):new RegExp("\\b"+t+"\\b").test(e.className)}function o(e,t,o){e.attachEvent?e.attachEvent("on"+t,o):e.addEventListener(t,o)}function s(e,t,o){e.detachEvent?e.detachEvent("on"+t,o):e.removeEventListener(t,o)}function n(e,s,n,l){o(l||document,s,function(o){for(var s,l=o.target||o.srcElement;l&&!(s=t(l,e));)l=l.parentElement;s&&n.call(l,o)})}if(document.querySelector){var l={selector:0,source:0,minChars:3,delay:150,offsetLeft:0,offsetTop:1,cache:1,menuClass:"",renderItem:function(e,t){t=t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&");var o=new RegExp("("+t.split(" ").join("|")+")","gi");return'
'+e.replace(o,"$1")+"
"},onSelect:function(){}};for(var c in e)e.hasOwnProperty(c)&&(l[c]=e[c]);for(var a="object"==typeof l.selector?[l.selector]:document.querySelectorAll(l.selector),u=0;u0?i.sc.scrollTop=n+i.sc.suggestionHeight+s-i.sc.maxHeight:0>n&&(i.sc.scrollTop=n+s)}else i.sc.scrollTop=0},o(window,"resize",i.updateSC),document.body.appendChild(i.sc),n("autocomplete-suggestion","mouseleave",function(){var e=i.sc.querySelector(".autocomplete-suggestion.selected");e&&setTimeout(function(){e.className=e.className.replace("selected","")},20)},i.sc),n("autocomplete-suggestion","mouseover",function(){var e=i.sc.querySelector(".autocomplete-suggestion.selected");e&&(e.className=e.className.replace("selected","")),this.className+=" selected"},i.sc),n("autocomplete-suggestion","mousedown",function(e){if(t(this,"autocomplete-suggestion")){var o=this.getAttribute("data-val");i.value=o,l.onSelect(e,o,this),i.sc.style.display="none"}},i.sc),i.blurHandler=function(){try{var e=document.querySelector(".autocomplete-suggestions:hover")}catch(t){var e=0}e?i!==document.activeElement&&setTimeout(function(){i.focus()},20):(i.last_val=i.value,i.sc.style.display="none",setTimeout(function(){i.sc.style.display="none"},350))},o(i,"blur",i.blurHandler);var r=function(e){var t=i.value;if(i.cache[t]=e,e.length&&t.length>=l.minChars){for(var o="",s=0;st||t>40)&&13!=t&&27!=t){var o=i.value;if(o.length>=l.minChars){if(o!=i.last_val){if(i.last_val=o,clearTimeout(i.timer),l.cache){if(o in i.cache)return void r(i.cache[o]);for(var s=1;si;){if(o-i>600){var s=o-i+1,u=n-i+1,l=Math.log(s),h=.5*Math.exp(2*l/3),c=.5*Math.sqrt(l*h*(s-h)/s)*(u-s/2<0?-1:1);t(r,n,Math.max(i,Math.floor(n-u*h/s+c)),Math.min(o,Math.floor(n+(s-u)*h/s+c)),a)}var p=r[n],f=i,d=o;for(e(r,i,n),a(r[o],p)>0&&e(r,i,o);f0;)d--}0===a(r[i],p)?e(r,i,d):e(r,++d,o),d<=n&&(i=d+1),n<=d&&(o=d-1)}}(t,n,i||0,o||t.length-1,a||r)}function e(t,e,r){var n=t[e];t[e]=t[r],t[r]=n}function r(t,e){return te?1:0}var n=function(t){void 0===t&&(t=9),this._maxEntries=Math.max(4,t),this._minEntries=Math.max(2,Math.ceil(.4*this._maxEntries)),this.clear()};function i(t,e,r){if(!r)return e.indexOf(t);for(var n=0;n=t.minX&&e.maxY>=t.minY}function d(t){return{children:t,height:1,leaf:!0,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0}}function _(e,r,n,i,o){for(var a=[r,n];a.length;)if(!((n=a.pop())-(r=a.pop())<=i)){var s=r+Math.ceil((n-r)/i/2)*i;t(e,s,r,n,o),a.push(r,s,s,n)}}return n.prototype.all=function(){return this._all(this.data,[])},n.prototype.search=function(t){var e=this.data,r=[];if(!f(t,e))return r;for(var n=this.toBBox,i=[];e;){for(var o=0;o=0&&i[e].children.length>this._maxEntries;)this._split(i,e),e--;this._adjustParentBBoxes(n,i,e)},n.prototype._split=function(t,e){var r=t[e],n=r.children.length,i=this._minEntries;this._chooseSplitAxis(r,i,n);var a=this._chooseSplitIndex(r,i,n),s=d(r.children.splice(a,r.children.length-a));s.height=r.height,s.leaf=r.leaf,o(r,this.toBBox),o(s,this.toBBox),e?t[e-1].children.push(s):this._splitRoot(r,s)},n.prototype._splitRoot=function(t,e){this.data=d([t,e]),this.data.height=t.height+1,this.data.leaf=!1,o(this.data,this.toBBox)},n.prototype._chooseSplitIndex=function(t,e,r){for(var n,i,o,s,u,l,c,p=1/0,f=1/0,d=e;d<=r-e;d++){var _=a(t,0,d,this.toBBox),g=a(t,d,r,this.toBBox),y=(i=_,o=g,s=Math.max(i.minX,o.minX),u=Math.max(i.minY,o.minY),l=Math.min(i.maxX,o.maxX),c=Math.min(i.maxY,o.maxY),Math.max(0,l-s)*Math.max(0,c-u)),v=h(_)+h(g);y=e;f--){var d=t.children[f];s(u,t.leaf?i(d):d),l+=c(u)}return l},n.prototype._adjustParentBBoxes=function(t,e,r){for(var n=r;n>=0;n--)s(e[n],t)},n.prototype._condense=function(t){for(var e=t.length-1,r=void 0;e>=0;e--)0===t[e].children.length?e>0?(r=t[e-1].children).splice(r.indexOf(t[e]),1):this.clear():o(t[e],this.toBBox)},n}()},function(t,e,r){ +/*! + * PEP v0.5.3 | https://github.com/jquery/PEP + * Copyright jQuery Foundation and other contributors | http://jquery.org/license + */ +t.exports=function(){"use strict";var t=["bubbles","cancelable","view","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","pageX","pageY"],e=[!1,!1,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0];function r(r,n){n=n||Object.create(null);var i=document.createEvent("Event");i.initEvent(r,n.bubbles||!1,n.cancelable||!1);for(var o,a=2;a0){var e=this.events;t.addEventListener("touchend",function(){t._scrollType=void 0,l.unlisten(t,e)})}else t._scrollType=void 0,l.unlisten(t,this.events);b(t).forEach(function(t){t._scrollType=void 0,l.unlisten(t,this.events)},this)},elementChanged:function(t,e){var r=t.getAttribute("touch-action"),n=this.touchActionToScrollType(r),i=this.touchActionToScrollType(e);"number"==typeof n&&"number"==typeof i?(t._scrollType=n,b(t).forEach(function(t){t._scrollType=n},this)):"number"==typeof i?this.elementRemoved(t):"number"==typeof n&&this.elementAdded(t)},scrollTypes:{UP:function(t){return t.includes("pan-y")||t.includes("pan-up")?1:0},DOWN:function(t){return t.includes("pan-y")||t.includes("pan-down")?2:0},LEFT:function(t){return t.includes("pan-x")||t.includes("pan-left")?4:0},RIGHT:function(t){return t.includes("pan-x")||t.includes("pan-right")?8:0}},touchActionToScrollType:function(t){if(t){if("auto"===t)return 15;if("none"===t)return 0;var e=t.split(" "),r=this.scrollTypes;return r.UP(e)|r.DOWN(e)|r.LEFT(e)|r.RIGHT(e)}},POINTER_TYPE:"touch",firstTouch:null,isPrimaryTouch:function(t){return this.firstTouch===t.identifier},setPrimaryTouch:function(t){(0===L.size||1===L.size&&L.has(1))&&(this.firstTouch=t.identifier,this.firstXY={X:t.clientX,Y:t.clientY},this.scrolling=!1)},removePrimaryPointer:function(t){t.isPrimary&&(this.firstTouch=null,this.firstXY=null)},typeToButtons:function(t){var e=0;return"touchstart"!==t&&"touchmove"!==t&&"touchforcechange"!==t||(e=1),e},touchToPointer:function(t){var e=this.currentTouchEvent,r=l.cloneEvent(t),n=r.pointerId=t.identifier+2;if(r.target=P[n]||I(r),r.bubbles=!0,r.cancelable=!0,r.button=0,r.buttons=this.typeToButtons(e.type),r.width=2*(t.radiusX||t.webkitRadiusX||0),r.height=2*(t.radiusY||t.webkitRadiusY||0),r.pressure=void 0!==t.force?t.force:void 0!==t.webkitForce?t.webkitForce:void 0,r.isPrimary=this.isPrimaryTouch(t),t.altitudeAngle){var i=Math.tan(t.altitudeAngle),o=180/Math.PI;r.tiltX=Math.atan(Math.cos(t.azimuthAngle)/i)*o,r.tiltY=Math.atan(Math.sin(t.azimuthAngle)/i)*o}else r.tiltX=0,r.tiltY=0;"stylus"===t.touchType?r.pointerType="pen":r.pointerType=this.POINTER_TYPE,r.altKey=e.altKey,r.ctrlKey=e.ctrlKey,r.metaKey=e.metaKey,r.shiftKey=e.shiftKey;var a=this;return r.preventDefault=function(){a.scrolling=!1,a.firstXY=null,e.preventDefault()},r},processTouches:function(t,e){var r=t.changedTouches;this.currentTouchEvent=t;for(var n,i=0;io:h?e=s>o&&a>0:c&&(e=s>o&&a<0),e||(u&&l?e=s0:l&&(e=s=e.length){var r=[];L.forEach(function(t,n){if(1!==n&&!this.findTouch(e,n-2)){var i=t.out;r.push(i)}},this),r.forEach(this.cancelOut,this)}},touchstart:function(t){this.vacuumTouches(t),this.setPrimaryTouch(t.changedTouches[0]),this.dedupSynthMouse(t),this.scrolling||this.processTouches(t,this.overDown)},overDown:function(t){L.set(t.pointerId,{target:t.target,out:t,outTarget:t.target}),l.enterOver(t),l.down(t)},touchforcechange:function(t){this.touchmove(t)},touchmove:function(t){this.scrolling||(this.shouldScroll(t)?(this.scrolling=!0,this.touchcancel(t)):("touchforcechange"!==t.type&&t.preventDefault(),this.processTouches(t,this.moveOverOut)))},moveOverOut:function(t){var e=t,r=L.get(e.pointerId);if(r){var n=r.out,i=r.outTarget;l.move(e),n&&i!==e.target&&(n.relatedTarget=e.target,e.relatedTarget=i,n.target=i,e.target?(l.leaveOut(n),l.enterOver(e)):(e.target=i,e.relatedTarget=null,this.cancelOut(e))),r.out=e,r.outTarget=e.target}},touchend:function(t){this.dedupSynthMouse(t),this.processTouches(t,this.upOut)},upOut:function(t){this.scrolling||(l.up(t),l.leaveOut(t)),this.cleanUpPointer(t)},touchcancel:function(t){this.processTouches(t,this.cancelOut)},cancelOut:function(t){l.cancel(t),l.leaveOut(t),this.cleanUpPointer(t)},cleanUpPointer:function(t){L.delete(t.pointerId),this.removePrimaryPointer(t)},dedupSynthMouse:function(t){var e=C.lastTouches,r=t.changedTouches[0];if(this.isPrimaryTouch(r)){var n={x:r.clientX,y:r.clientY};e.push(n);var i=function(t,e){var r=t.indexOf(e);r>-1&&t.splice(r,1)}.bind(null,e,n);setTimeout(i,2500)}}};R=new y(M.elementAdded,M.elementRemoved,M.elementChanged,M);var F,A,N,D=l.pointermap,G=window.MSPointerEvent&&"number"==typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE,j={events:["MSPointerDown","MSPointerMove","MSPointerUp","MSPointerOut","MSPointerOver","MSPointerCancel","MSGotPointerCapture","MSLostPointerCapture"],register:function(t){l.listen(t,this.events)},unregister:function(t){l.unlisten(t,this.events)},POINTER_TYPES:["","unavailable","touch","pen","mouse"],prepareEvent:function(t){var e=t;return G&&((e=l.cloneEvent(t)).pointerType=this.POINTER_TYPES[t.pointerType]),e},cleanup:function(t){D.delete(t)},MSPointerDown:function(t){D.set(t.pointerId,t);var e=this.prepareEvent(t);l.down(e)},MSPointerMove:function(t){var e=this.prepareEvent(t);l.move(e)},MSPointerUp:function(t){var e=this.prepareEvent(t);l.up(e),this.cleanup(t.pointerId)},MSPointerOut:function(t){var e=this.prepareEvent(t);l.leaveOut(e)},MSPointerOver:function(t){var e=this.prepareEvent(t);l.enterOver(e)},MSPointerCancel:function(t){var e=this.prepareEvent(t);l.cancel(e),this.cleanup(t.pointerId)},MSLostPointerCapture:function(t){var e=l.makeEvent("lostpointercapture",t);l.dispatchEvent(e)},MSGotPointerCapture:function(t){var e=l.makeEvent("gotpointercapture",t);l.dispatchEvent(e)}};function k(t){if(!l.pointermap.has(t)){var e=new Error("NotFoundError");throw e.name="NotFoundError",e}}function U(t){for(var e=t.parentNode;e&&e!==t.ownerDocument;)e=e.parentNode;if(!e){var r=new Error("InvalidStateError");throw r.name="InvalidStateError",r}}function B(t){return 0!==l.pointermap.get(t).buttons}return window.navigator.msPointerEnabled?(F=function(t){k(t),U(this),B(t)&&(l.setCapture(t,this,!0),this.msSetPointerCapture(t))},A=function(t){k(t),l.releaseCapture(t,!0),this.msReleasePointerCapture(t)}):(F=function(t){k(t),U(this),B(t)&&l.setCapture(t,this)},A=function(t){k(t),l.releaseCapture(t)}),N=function(t){return!!l.captureInfo[t]},function(){if(T){m.forEach(function(t){E+=t.selector+v(t.value)+"\n",S&&(E+=function(t){return"body /shadow-deep/ "+t}(t.selector)+v(t.value)+"\n")});var t=document.createElement("style");t.textContent=E,document.head.appendChild(t)}}(),function(){if(!window.PointerEvent){if(window.PointerEvent=r,window.navigator.msPointerEnabled){var t=window.navigator.msMaxTouchPoints;Object.defineProperty(window.navigator,"maxTouchPoints",{value:t,enumerable:!0}),l.registerSource("ms",j)}else Object.defineProperty(window.navigator,"maxTouchPoints",{value:0,enumerable:!0}),l.registerSource("mouse",C),void 0!==window.ontouchstart&&l.registerSource("touch",M);l.register(document)}}(),window.Element&&!Element.prototype.setPointerCapture&&Object.defineProperties(Element.prototype,{setPointerCapture:{value:F},releasePointerCapture:{value:A},hasPointerCapture:{value:N}}),{dispatcher:l,Installer:y,PointerEvent:r,PointerMap:n,targetFinding:h}}()},function(t,e,r){"use strict";t.exports=i;var n=r(4);function i(t){this.buf=ArrayBuffer.isView&&ArrayBuffer.isView(t)?t:new Uint8Array(t||0),this.pos=0,this.type=0,this.length=this.buf.length}i.Varint=0,i.Fixed64=1,i.Bytes=2,i.Fixed32=5;var o="undefined"==typeof TextDecoder?null:new TextDecoder("utf8");function a(t){return t.type===i.Bytes?t.readVarint()+t.pos:t.pos+1}function s(t,e,r){return r?4294967296*e+(t>>>0):4294967296*(e>>>0)+(t>>>0)}function u(t,e,r){var n=e<=16383?1:e<=2097151?2:e<=268435455?3:Math.floor(Math.log(e)/(7*Math.LN2));r.realloc(n);for(var i=r.pos-1;i>=t;i--)r.buf[i+n]=r.buf[i]}function l(t,e){for(var r=0;r>>8,t[r+2]=e>>>16,t[r+3]=e>>>24}function E(t,e){return(t[e]|t[e+1]<<8|t[e+2]<<16)+(t[e+3]<<24)}i.prototype={destroy:function(){this.buf=null},readFields:function(t,e,r){for(r=r||this.length;this.pos>3,o=this.pos;this.type=7&n,t(i,e,this),this.pos===o&&this.skip(n)}return e},readMessage:function(t,e){return this.readFields(t,e,this.readVarint()+this.pos)},readFixed32:function(){var t=v(this.buf,this.pos);return this.pos+=4,t},readSFixed32:function(){var t=E(this.buf,this.pos);return this.pos+=4,t},readFixed64:function(){var t=v(this.buf,this.pos)+4294967296*v(this.buf,this.pos+4);return this.pos+=8,t},readSFixed64:function(){var t=v(this.buf,this.pos)+4294967296*E(this.buf,this.pos+4);return this.pos+=8,t},readFloat:function(){var t=n.read(this.buf,this.pos,!0,23,4);return this.pos+=4,t},readDouble:function(){var t=n.read(this.buf,this.pos,!0,52,8);return this.pos+=8,t},readVarint:function(t){var e,r,n=this.buf;return e=127&(r=n[this.pos++]),r<128?e:(e|=(127&(r=n[this.pos++]))<<7,r<128?e:(e|=(127&(r=n[this.pos++]))<<14,r<128?e:(e|=(127&(r=n[this.pos++]))<<21,r<128?e:function(t,e,r){var n,i,o=r.buf;if(i=o[r.pos++],n=(112&i)>>4,i<128)return s(t,n,e);if(i=o[r.pos++],n|=(127&i)<<3,i<128)return s(t,n,e);if(i=o[r.pos++],n|=(127&i)<<10,i<128)return s(t,n,e);if(i=o[r.pos++],n|=(127&i)<<17,i<128)return s(t,n,e);if(i=o[r.pos++],n|=(127&i)<<24,i<128)return s(t,n,e);if(i=o[r.pos++],n|=(1&i)<<31,i<128)return s(t,n,e);throw new Error("Expected varint not more than 10 bytes")}(e|=(15&(r=n[this.pos]))<<28,t,this))))},readVarint64:function(){return this.readVarint(!0)},readSVarint:function(){var t=this.readVarint();return t%2==1?(t+1)/-2:t/2},readBoolean:function(){return Boolean(this.readVarint())},readString:function(){var t=this.readVarint()+this.pos,e=this.pos;return this.pos=t,t-e>=12&&o?function(t,e,r){return o.decode(t.subarray(e,r))}(this.buf,e,t):function(t,e,r){var n="",i=e;for(;i239?4:u>223?3:u>191?2:1;if(i+h>r)break;1===h?u<128&&(l=u):2===h?128==(192&(o=t[i+1]))&&(l=(31&u)<<6|63&o)<=127&&(l=null):3===h?(o=t[i+1],a=t[i+2],128==(192&o)&&128==(192&a)&&((l=(15&u)<<12|(63&o)<<6|63&a)<=2047||l>=55296&&l<=57343)&&(l=null)):4===h&&(o=t[i+1],a=t[i+2],s=t[i+3],128==(192&o)&&128==(192&a)&&128==(192&s)&&((l=(15&u)<<18|(63&o)<<12|(63&a)<<6|63&s)<=65535||l>=1114112)&&(l=null)),null===l?(l=65533,h=1):l>65535&&(l-=65536,n+=String.fromCharCode(l>>>10&1023|55296),l=56320|1023&l),n+=String.fromCharCode(l),i+=h}return n}(this.buf,e,t)},readBytes:function(){var t=this.readVarint()+this.pos,e=this.buf.subarray(this.pos,t);return this.pos=t,e},readPackedVarint:function(t,e){if(this.type!==i.Bytes)return t.push(this.readVarint(e));var r=a(this);for(t=t||[];this.pos127;);else if(e===i.Bytes)this.pos=this.readVarint()+this.pos;else if(e===i.Fixed32)this.pos+=4;else{if(e!==i.Fixed64)throw new Error("Unimplemented type: "+e);this.pos+=8}},writeTag:function(t,e){this.writeVarint(t<<3|e)},realloc:function(t){for(var e=this.length||16;e268435455||t<0?function(t,e){var r,n;t>=0?(r=t%4294967296|0,n=t/4294967296|0):(n=~(-t/4294967296),4294967295^(r=~(-t%4294967296))?r=r+1|0:(r=0,n=n+1|0));if(t>=0x10000000000000000||t<-0x10000000000000000)throw new Error("Given varint doesn't fit into 10 bytes");e.realloc(10),function(t,e,r){r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos]=127&t}(r,0,e),function(t,e){var r=(7&t)<<4;if(e.buf[e.pos++]|=r|((t>>>=3)?128:0),!t)return;if(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),!t)return;if(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),!t)return;if(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),!t)return;if(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),!t)return;e.buf[e.pos++]=127&t}(n,e)}(t,this):(this.realloc(4),this.buf[this.pos++]=127&t|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=t>>>7&127))))},writeSVarint:function(t){this.writeVarint(t<0?2*-t-1:2*t)},writeBoolean:function(t){this.writeVarint(Boolean(t))},writeString:function(t){t=String(t),this.realloc(4*t.length),this.pos++;var e=this.pos;this.pos=function(t,e,r){for(var n,i,o=0;o55295&&n<57344){if(!i){n>56319||o+1===e.length?(t[r++]=239,t[r++]=191,t[r++]=189):i=n;continue}if(n<56320){t[r++]=239,t[r++]=191,t[r++]=189,i=n;continue}n=i-55296<<10|n-56320|65536,i=null}else i&&(t[r++]=239,t[r++]=191,t[r++]=189,i=null);n<128?t[r++]=n:(n<2048?t[r++]=n>>6|192:(n<65536?t[r++]=n>>12|224:(t[r++]=n>>18|240,t[r++]=n>>12&63|128),t[r++]=n>>6&63|128),t[r++]=63&n|128)}return r}(this.buf,t,this.pos);var r=this.pos-e;r>=128&&u(e,r,this),this.pos=e-1,this.writeVarint(r),this.pos+=r},writeFloat:function(t){this.realloc(4),n.write(this.buf,t,this.pos,!0,23,4),this.pos+=4},writeDouble:function(t){this.realloc(8),n.write(this.buf,t,this.pos,!0,52,8),this.pos+=8},writeBytes:function(t){var e=t.length;this.writeVarint(e),this.realloc(e);for(var r=0;r=128&&u(r,n,this),this.pos=r-1,this.writeVarint(n),this.pos+=n},writeMessage:function(t,e,r){this.writeTag(t,i.Bytes),this.writeRawMessage(e,r)},writePackedVarint:function(t,e){e.length&&this.writeMessage(t,l,e)},writePackedSVarint:function(t,e){e.length&&this.writeMessage(t,h,e)},writePackedBoolean:function(t,e){e.length&&this.writeMessage(t,f,e)},writePackedFloat:function(t,e){e.length&&this.writeMessage(t,c,e)},writePackedDouble:function(t,e){e.length&&this.writeMessage(t,p,e)},writePackedFixed32:function(t,e){e.length&&this.writeMessage(t,d,e)},writePackedSFixed32:function(t,e){e.length&&this.writeMessage(t,_,e)},writePackedFixed64:function(t,e){e.length&&this.writeMessage(t,g,e)},writePackedSFixed64:function(t,e){e.length&&this.writeMessage(t,y,e)},writeBytesField:function(t,e){this.writeTag(t,i.Bytes),this.writeBytes(e)},writeFixed32Field:function(t,e){this.writeTag(t,i.Fixed32),this.writeFixed32(e)},writeSFixed32Field:function(t,e){this.writeTag(t,i.Fixed32),this.writeSFixed32(e)},writeFixed64Field:function(t,e){this.writeTag(t,i.Fixed64),this.writeFixed64(e)},writeSFixed64Field:function(t,e){this.writeTag(t,i.Fixed64),this.writeSFixed64(e)},writeVarintField:function(t,e){this.writeTag(t,i.Varint),this.writeVarint(e)},writeSVarintField:function(t,e){this.writeTag(t,i.Varint),this.writeSVarint(e)},writeStringField:function(t,e){this.writeTag(t,i.Bytes),this.writeString(e)},writeFloatField:function(t,e){this.writeTag(t,i.Fixed32),this.writeFloat(e)},writeDoubleField:function(t,e){this.writeTag(t,i.Fixed64),this.writeDouble(e)},writeBooleanField:function(t,e){this.writeVarintField(t,Boolean(e))}}},function(t,e,r){var n=r(5);e.Processor=n},function(t,e){e.read=function(t,e,r,n,i){var o,a,s=8*i-n-1,u=(1<>1,h=-7,c=r?i-1:0,p=r?-1:1,f=t[e+c];for(c+=p,o=f&(1<<-h)-1,f>>=-h,h+=s;h>0;o=256*o+t[e+c],c+=p,h-=8);for(a=o&(1<<-h)-1,o>>=-h,h+=n;h>0;a=256*a+t[e+c],c+=p,h-=8);if(0===o)o=1-l;else{if(o===u)return a?NaN:1/0*(f?-1:1);a+=Math.pow(2,n),o-=l}return(f?-1:1)*a*Math.pow(2,o-n)},e.write=function(t,e,r,n,i,o){var a,s,u,l=8*o-i-1,h=(1<>1,p=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,f=n?0:o-1,d=n?1:-1,_=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(s=isNaN(e)?1:0,a=h):(a=Math.floor(Math.log(e)/Math.LN2),e*(u=Math.pow(2,-a))<1&&(a--,u*=2),(e+=a+c>=1?p/u:p*Math.pow(2,1-c))*u>=2&&(a++,u/=2),a+c>=h?(s=0,a=h):a+c>=1?(s=(e*u-1)*Math.pow(2,i),a+=c):(s=e*Math.pow(2,c-1)*Math.pow(2,i),a=0));i>=8;t[r+f]=255&s,f+=d,s/=256,i-=8);for(a=a<0;t[r+f]=255&a,f+=d,a/=256,l-=8);t[r+f-d]|=128*_}},function(t,e,r){var n=r(6).newImageData;function i(t){var e=!0;try{new ImageData(10,10)}catch(t){e=!1}function r(t,r,n){return e?new ImageData(t,r,n):{data:t,width:r,height:n}}return function(e){var n,i,o=e.buffers,a=e.meta,s=e.imageOps,u=e.width,l=e.height,h=o.length,c=o[0].byteLength;if(s){var p=new Array(h);for(i=0;ithis._maxQueueLength;)this._queue.shift().callback(null,null)},a.prototype._dispatch=function(){if(0===this._running&&this._queue.length>0){var t=this._job=this._queue.shift(),e=t.inputs[0].width,r=t.inputs[0].height,n=t.inputs.map(function(t){return t.data.buffer}),i=this._workers.length;if(this._running=i,1===i)this._workers[0].postMessage({buffers:n,meta:t.meta,imageOps:this._imageOps,width:e,height:r},n);else for(var o=t.inputs[0].data.length,a=4*Math.ceil(o/4/i),s=0;se?1:t=0}function S(t,e,r){var n=t.length;if(t[0]<=e)return 0;if(e<=t[n-1])return n-1;var i=void 0;if(r>0){for(i=1;i>>0,i=0;i0},e.prototype.removeEventListener=function(t,e){var r=this.listeners_[t];if(r){var n=r.indexOf(e);-1!==n&&(t in this.pendingRemovals_?(r[n]=b,++this.pendingRemovals_[t]):(r.splice(n,1),0===r.length&&delete this.listeners_[t]))}},e}(m),N={CHANGE:"change",ERROR:"error",BLUR:"blur",CLEAR:"clear",CONTEXTMENU:"contextmenu",CLICK:"click",DBLCLICK:"dblclick",DRAGENTER:"dragenter",DRAGOVER:"dragover",DROP:"drop",FOCUS:"focus",KEYDOWN:"keydown",KEYPRESS:"keypress",LOAD:"load",RESIZE:"resize",WHEEL:"wheel"},D=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();var G=function(t){function e(){var e=t.call(this)||this;return e.revision_=0,e}return D(e,t),e.prototype.changed=function(){++this.revision_,this.dispatchEvent(N.CHANGE)},e.prototype.getRevision=function(){return this.revision_},e.prototype.on=function(t,e){if(Array.isArray(t)){for(var r=t.length,n=new Array(r),i=0;i0;)this.pop()},e.prototype.extend=function(t){for(var e=0,r=t.length;ei&&(u|=Q.RIGHT),so&&(u|=Q.ABOVE),u===Q.UNKNOWN&&(u=Q.INTERSECTING),u}function st(){return[1/0,1/0,-1/0,-1/0]}function ut(t,e,r,n,i){return i?(i[0]=t,i[1]=e,i[2]=r,i[3]=n,i):[t,e,r,n]}function lt(t){return ut(1/0,1/0,-1/0,-1/0,t)}function ht(t,e){var r=t[0],n=t[1];return ut(r,n,r,n,e)}function ct(t,e,r,n,i){return gt(lt(i),t,e,r,n)}function pt(t,e){return t[0]==e[0]&&t[2]==e[2]&&t[1]==e[1]&&t[3]==e[3]}function ft(t,e){return e[0]t[2]&&(t[2]=e[2]),e[1]t[3]&&(t[3]=e[3]),t}function dt(t,e){e[0]t[2]&&(t[2]=e[0]),e[1]t[3]&&(t[3]=e[1])}function _t(t,e){for(var r=0,n=e.length;re[0]?n[0]=t[0]:n[0]=e[0],t[1]>e[1]?n[1]=t[1]:n[1]=e[1],t[2]=e[0]&&t[1]<=e[3]&&t[3]>=e[1]}function Lt(t){return t[2]1?(r=i,n=o):u>0&&(r+=a*u,n+=s*u)}return Yt(t,e,r,n)}function Yt(t,e,r,n){var i=r-t,o=n-e;return i*i+o*o}function zt(t){return 180*t/Math.PI}function Xt(t){return t*Math.PI/180}function Vt(t,e){var r=t%e;return r*e<0?r+e:r}function Wt(t,e,r){return t+r*(e-t)} +/** + * @license + * Latitude/longitude spherical geodesy formulae taken from + * http://www.movable-type.co.uk/scripts/latlong.html + * Licensed under CC-BY-3.0. + */var Zt=6371008.8;function Kt(t,e,r){var n=r||Zt,i=Xt(t[1]),o=Xt(e[1]),a=(o-i)/2,s=Xt(e[0]-t[0])/2,u=Math.sin(a)*Math.sin(a)+Math.sin(s)*Math.sin(s)*Math.cos(i)*Math.cos(o);return 2*n*Math.atan2(Math.sqrt(u),Math.sqrt(1-u))}function Ht(t,e){for(var r=0,n=0,i=t.length;n1?r:2,o=e;void 0===o&&(o=i>2?t.slice():new Array(n));for(var a=ie,s=0;sa?u=a:u<-a&&(u=-a),o[s+1]=u}return o}function he(t,e,r){var n=t.length,i=r>1?r:2,o=e;void 0===o&&(o=i>2?t.slice():new Array(n));for(var a=0;a=2;--l)a[s+l]=e[s+l]}return a}}function Pe(t,e,r,n){var i=we(t),o=we(e);ye(i,o,Ce(r)),ye(o,i,Ce(n))}function Ie(t,e){if(t===e)return!0;var r=t.getUnits()===e.getUnits();return t.getCode()===e.getCode()?r:be(t,e)===Ee&&r}function be(t,e){var r=ve(t.getCode(),e.getCode());return r||(r=Te),r}function Le(t,e){return be(we(t),we(e))}function Me(t,e,r){return Le(e,r)(t,void 0,t.length)}function Fe(t,e,r){return Ft(t,Le(e,r))}var Ae,Ne,De,Ge=null;function je(){return Ge}function ke(t,e){return Ge?Me(t,e,Ge):t}function Ue(t,e){return Ge?Me(t,Ge,e):t}function Be(t,e){return Ge?Fe(t,e,Ge):t}function Ye(t,e){return Ge?Fe(t,Ge,e):t}Oe(ue),Oe(_e),Ae=ue,Ne=le,De=he,_e.forEach(function(t){Ae.forEach(function(e){ye(t,e,Ne),ye(e,t,De)})});var ze=new Array(6);function Xe(t){return We(t,1,0,0,1,0,0)}function Ve(t,e){var r=t[0],n=t[1],i=t[2],o=t[3],a=t[4],s=t[5],u=e[0],l=e[1],h=e[2],c=e[3],p=e[4],f=e[5];return t[0]=r*u+i*l,t[1]=n*u+o*l,t[2]=r*h+i*c,t[3]=n*h+o*c,t[4]=r*p+i*f+a,t[5]=n*p+o*f+s,t}function We(t,e,r,n,i,o,a){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t[4]=o,t[5]=a,t}function Ze(t,e){var r=e[0],n=e[1];return e[0]=t[0]*r+t[2]*n+t[4],e[1]=t[1]*r+t[3]*n+t[5],e}function Ke(t,e,r){return Ve(t,We(ze,e,0,0,r,0,0))}function He(t,e,r,n,i,o,a,s){var u=Math.sin(o),l=Math.cos(o);return t[0]=n*l,t[1]=i*u,t[2]=-n*u,t[3]=i*l,t[4]=a*n*l-s*n*u+e,t[5]=a*i*u+s*i*l+r,t}function qe(t,e){var r,n=(r=e)[0]*r[3]-r[1]*r[2];K(0!==n,32);var i=e[0],o=e[1],a=e[2],s=e[3],u=e[4],l=e[5];return t[0]=s/n,t[1]=-o/n,t[2]=-a/n,t[3]=i/n,t[4]=(a*l-s*u)/n,t[5]=-(i*l-o*u)/n,t}function Je(t){return"matrix("+t.join(", ")+")"}var Qe=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),$e=[1,0,0,1,0,0],tr=function(t){function e(){var e,r,n,i,o,a=t.call(this)||this;return a.extent_=[1/0,1/0,-1/0,-1/0],a.extentRevision_=-1,a.simplifiedGeometryMaxMinSquaredTolerance=0,a.simplifiedGeometryRevision=0,a.simplifyTransformedInternal=(e=function(t,e,r){if(!r)return this.getSimplifiedGeometry(e);var n=this.clone();return n.applyTransform(r),n.getSimplifiedGeometry(e)},o=!1,function(){var t=Array.prototype.slice.call(arguments);return o&&this===i&&R(t,n)||(o=!0,i=this,n=t,r=e.apply(this,arguments)),r}),a}return Qe(e,t),e.prototype.simplifyTransformed=function(t,e){return this.simplifyTransformedInternal(this.getRevision(),t,e)},e.prototype.clone=function(){return n()},e.prototype.closestPointXY=function(t,e,r,i){return n()},e.prototype.containsXY=function(t,e){var r=this.getClosestPoint([t,e]);return r[0]===t&&r[1]===e},e.prototype.getClosestPoint=function(t,e){var r=e||[NaN,NaN];return this.closestPointXY(t[0],t[1],r,1/0),r},e.prototype.intersectsCoordinate=function(t){return this.containsXY(t[0],t[1])},e.prototype.computeExtent=function(t){return n()},e.prototype.getExtent=function(t){return this.extentRevision_!=this.getRevision()&&(this.extent_=this.computeExtent(this.extent_),this.extentRevision_=this.getRevision()),function(t,e){return e?(e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e):t}(this.extent_,t)},e.prototype.rotate=function(t,e){n()},e.prototype.scale=function(t,e,r){n()},e.prototype.simplify=function(t){return this.getSimplifiedGeometry(t*t)},e.prototype.getSimplifiedGeometry=function(t){return n()},e.prototype.getType=function(){return n()},e.prototype.applyTransform=function(t){n()},e.prototype.intersectsExtent=function(t){return n()},e.prototype.translate=function(t,e){n()},e.prototype.transform=function(t,e){var r=we(t),n=r.getUnits()==te.TILE_PIXELS?function(t,n,i){var o=r.getExtent(),a=r.getWorldExtent(),s=Ot(a)/Ot(o);return He($e,a[0],a[3],s,-s,0,0,0),Dt(t,0,t.length,i,$e,n),Le(r,e)(t,n,i)}:Le(r,e);return this.applyTransform(n),this},e}(z),er=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function rr(t){var e;return t==At.XY?e=2:t==At.XYZ||t==At.XYM?e=3:t==At.XYZM&&(e=4),e}var nr=function(t){function e(){var e=t.call(this)||this;return e.layout=At.XY,e.stride=2,e.flatCoordinates=null,e}return er(e,t),e.prototype.computeExtent=function(t){return ct(this.flatCoordinates,0,this.flatCoordinates.length,this.stride,t)},e.prototype.getCoordinates=function(){return n()},e.prototype.getFirstCoordinate=function(){return this.flatCoordinates.slice(0,this.stride)},e.prototype.getFlatCoordinates=function(){return this.flatCoordinates},e.prototype.getLastCoordinate=function(){return this.flatCoordinates.slice(this.flatCoordinates.length-this.stride)},e.prototype.getLayout=function(){return this.layout},e.prototype.getSimplifiedGeometry=function(t){if(this.simplifiedGeometryRevision!==this.getRevision()&&(this.simplifiedGeometryMaxMinSquaredTolerance=0,this.simplifiedGeometryRevision=this.getRevision()),t<0||0!==this.simplifiedGeometryMaxMinSquaredTolerance&&t<=this.simplifiedGeometryMaxMinSquaredTolerance)return this;var e=this.getSimplifiedGeometryInternal(t);return e.getFlatCoordinates().length1)s=r;else{if(p>0){for(var f=0;fi&&(i=l),o=s,a=u}return i}function ur(t,e,r,n,i){for(var o=0,a=r.length;o0;){for(var c=l.pop(),p=l.pop(),f=0,d=t[p],_=t[p+1],g=t[c],y=t[c+1],v=p+n;vf&&(h=v,f=m)}f>i&&(u[(h-e)/n]=1,p+n0&&_>f)&&(d<0&&g0&&g>d)?(s=c,u=p):(o[a++]=s,o[a++]=u,l=s,h=u,s=c,u=p)}}return o[a++]=s,o[a++]=u,a}function Tr(t,e,r,n,i,o,a,s){for(var u=0,l=r.length;uo&&(l-s)*(o-u)-(i-s)*(h-u)>0&&a++:h<=o&&(l-s)*(o-u)-(i-s)*(h-u)<0&&a--,s=l,u=h}return 0!==a}function Pr(t,e,r,n,i,o){if(0===r.length)return!1;if(!Cr(t,e,r[0],n,i,o))return!1;for(var a=1,s=r.length;aT&&Pr(t,e,r,n,l=(h+c)/2,d)&&(m=l,T=S),h=c}return isNaN(m)&&(m=i[o]),a?(a.push(m,d,T),a):[m,d,T]}function br(t,e,r,n,i){for(var o=[],a=0,s=r.length;a=i[0]&&o[2]<=i[2]||(o[1]>=i[1]&&o[3]<=i[3]||Lr(t,e,r,n,function(t,e){return function(t,e,r){var n=!1,i=at(t,e),o=at(t,r);if(i===Q.INTERSECTING||o===Q.INTERSECTING)n=!0;else{var a=t[0],s=t[1],u=t[2],l=t[3],h=e[0],c=e[1],p=r[0],f=r[1],d=(f-c)/(p-h),_=void 0,g=void 0;o&Q.ABOVE&&!(i&Q.ABOVE)&&(n=(_=p-(f-l)/d)>=a&&_<=u),n||!(o&Q.RIGHT)||i&Q.RIGHT||(n=(g=f-(p-u)*d)>=s&&g<=l),n||!(o&Q.BELOW)||i&Q.BELOW||(n=(_=p-(f-s)/d)>=a&&_<=u),n||!(o&Q.LEFT)||i&Q.LEFT||(n=(g=f-(p-a)*d)>=s&&g<=l)}return n}(i,t,e)}))))}function Fr(t,e,r,n,i){if(!function(t,e,r,n,i){return!!Mr(t,e,r,n,i)||(!!Cr(t,e,r,n,i[0],i[1])||(!!Cr(t,e,r,n,i[0],i[3])||(!!Cr(t,e,r,n,i[2],i[1])||!!Cr(t,e,r,n,i[2],i[3]))))}(t,e,r[0],n,i))return!1;if(1===r.length)return!0;for(var o=1,a=r.length;o0}function Dr(t,e,r,n,i){for(var o=void 0!==i&&i,a=0,s=r.length;a0&&this.points_[r+2]>t;)r-=3;var n=this.points_[e+2]-this.points_[r+2];if(n<1e3/60)return!1;var i=this.points_[e]-this.points_[r],o=this.points_[e+1]-this.points_[r+1];return this.angle_=Math.atan2(o,i),this.initialVelocity_=Math.sqrt(i*i+o*o)/n,this.initialVelocity_>this.minVelocity_},t.prototype.getDistance=function(){return(this.minVelocity_-this.initialVelocity_)/this.decay_},t.prototype.getAngle=function(){return this.angle_},t}(),Jr=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Qr=function(t){function e(e,r,n){var i=t.call(this,e)||this;return i.map=r,i.frameState=void 0!==n?n:null,i}return Jr(e,t),e}(M),$r=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),tn=function(t){function e(e,r,n,i,o){var a=t.call(this,e,r,o)||this;return a.originalEvent=n,a.pixel_=null,a.coordinate_=null,a.dragging=void 0!==i&&i,a}return $r(e,t),Object.defineProperty(e.prototype,"pixel",{get:function(){return this.pixel_||(this.pixel_=this.map.getEventPixel(this.originalEvent)),this.pixel_},set:function(t){this.pixel_=t},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"coordinate",{get:function(){return this.coordinate_||(this.coordinate_=this.map.getCoordinateFromPixel(this.pixel)),this.coordinate_},set:function(t){this.coordinate_=t},enumerable:!0,configurable:!0}),e.prototype.preventDefault=function(){t.prototype.preventDefault.call(this),this.originalEvent.preventDefault()},e.prototype.stopPropagation=function(){t.prototype.stopPropagation.call(this),this.originalEvent.stopPropagation()},e}(Qr),en=(r(1),"undefined"!=typeof navigator?navigator.userAgent.toLowerCase():""),rn=-1!==en.indexOf("firefox"),nn=(-1!==en.indexOf("safari")&&en.indexOf("chrom"),-1!==en.indexOf("webkit")&&-1==en.indexOf("edge")),on=-1!==en.indexOf("macintosh"),an=window.devicePixelRatio||1,sn="undefined"!=typeof Image&&Image.prototype.decode,un={SINGLECLICK:"singleclick",CLICK:N.CLICK,DBLCLICK:N.DBLCLICK,POINTERDRAG:"pointerdrag",POINTERMOVE:"pointermove",POINTERDOWN:"pointerdown",POINTERUP:"pointerup",POINTEROVER:"pointerover",POINTEROUT:"pointerout",POINTERENTER:"pointerenter",POINTERLEAVE:"pointerleave",POINTERCANCEL:"pointercancel"},ln=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),hn=function(t){function e(e,r,n,i,o){var a=t.call(this,e,r,n,i,o)||this;return a.pointerEvent=n,a}return ln(e,t),e}(tn),cn={POINTERMOVE:"pointermove",POINTERDOWN:"pointerdown",POINTERUP:"pointerup",POINTEROVER:"pointerover",POINTEROUT:"pointerout",POINTERENTER:"pointerenter",POINTERLEAVE:"pointerleave",POINTERCANCEL:"pointercancel"},pn=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),fn=function(t){function e(e,r){var n=t.call(this,e)||this;n.map_=e,n.clickTimeoutId_,n.dragging_=!1,n.dragListenerKeys_=[],n.moveTolerance_=r?r*an:an,n.down_=null;var i=n.map_.getViewport();return n.activePointers_=0,n.trackedTouches_={},n.element_=i,n.pointerdownListenerKey_=g(i,cn.POINTERDOWN,n.handlePointerDown_,n),n.relayedListenerKey_=g(i,cn.POINTERMOVE,n.relayEvent_,n),n}return pn(e,t),e.prototype.emulateClick_=function(t){var e=new hn(un.CLICK,this.map_,t);this.dispatchEvent(e),void 0!==this.clickTimeoutId_?(clearTimeout(this.clickTimeoutId_),this.clickTimeoutId_=void 0,e=new hn(un.DBLCLICK,this.map_,t),this.dispatchEvent(e)):this.clickTimeoutId_=setTimeout(function(){this.clickTimeoutId_=void 0;var e=new hn(un.SINGLECLICK,this.map_,t);this.dispatchEvent(e)}.bind(this),250)},e.prototype.updateActivePointers_=function(t){var e=t;e.type==un.POINTERUP||e.type==un.POINTERCANCEL?delete this.trackedTouches_[e.pointerId]:e.type==un.POINTERDOWN&&(this.trackedTouches_[e.pointerId]=!0),this.activePointers_=Object.keys(this.trackedTouches_).length},e.prototype.handlePointerUp_=function(t){this.updateActivePointers_(t);var e=new hn(un.POINTERUP,this.map_,t);this.dispatchEvent(e),e.propagationStopped||this.dragging_||!this.isMouseActionButton_(t)||this.emulateClick_(this.down_),0===this.activePointers_&&(this.dragListenerKeys_.forEach(v),this.dragListenerKeys_.length=0,this.dragging_=!1,this.down_=null)},e.prototype.isMouseActionButton_=function(t){return 0===t.button},e.prototype.handlePointerDown_=function(t){this.updateActivePointers_(t);var e=new hn(un.POINTERDOWN,this.map_,t);this.dispatchEvent(e),this.down_=t,0===this.dragListenerKeys_.length&&this.dragListenerKeys_.push(g(document,un.POINTERMOVE,this.handlePointerMove_,this),g(document,un.POINTERUP,this.handlePointerUp_,this),g(this.element_,un.POINTERCANCEL,this.handlePointerUp_,this))},e.prototype.handlePointerMove_=function(t){if(this.isMoving_(t)){this.dragging_=!0;var e=new hn(un.POINTERDRAG,this.map_,t,this.dragging_);this.dispatchEvent(e)}},e.prototype.relayEvent_=function(t){var e=!(!this.down_||!this.isMoving_(t));this.dispatchEvent(new hn(t.type,this.map_,t,e))},e.prototype.isMoving_=function(t){return this.dragging_||Math.abs(t.clientX-this.down_.clientX)>this.moveTolerance_||Math.abs(t.clientY-this.down_.clientY)>this.moveTolerance_},e.prototype.disposeInternal=function(){this.relayedListenerKey_&&(v(this.relayedListenerKey_),this.relayedListenerKey_=null),this.pointerdownListenerKey_&&(v(this.pointerdownListenerKey_),this.pointerdownListenerKey_=null),this.dragListenerKeys_.forEach(v),this.dragListenerKeys_.length=0,this.element_=null,t.prototype.disposeInternal.call(this)},e}(A),dn="postrender",_n="movestart",gn="moveend",yn={LAYERGROUP:"layergroup",SIZE:"size",TARGET:"target",VIEW:"view"},vn="prerender",mn="postrender",En="precompose",Tn="postcompose",Sn="rendercomplete",wn={IDLE:0,LOADING:1,LOADED:2,ERROR:3,EMPTY:4,ABORT:5},xn=function(){function t(t,e){this.priorityFunction_=t,this.keyFunction_=e,this.elements_=[],this.priorities_=[],this.queuedElements_={}}return t.prototype.clear=function(){this.elements_.length=0,this.priorities_.length=0,f(this.queuedElements_)},t.prototype.dequeue=function(){var t=this.elements_,e=this.priorities_,r=t[0];1==t.length?(t.length=0,e.length=0):(t[0]=t.pop(),e[0]=e.pop(),this.siftUp_(0));var n=this.keyFunction_(r);return delete this.queuedElements_[n],r},t.prototype.enqueue=function(t){K(!(this.keyFunction_(t)in this.queuedElements_),31);var e=this.priorityFunction_(t);return e!=1/0&&(this.elements_.push(t),this.priorities_.push(e),this.queuedElements_[this.keyFunction_(t)]=!0,this.siftDown_(0,this.elements_.length-1),!0)},t.prototype.getCount=function(){return this.elements_.length},t.prototype.getLeftChildIndex_=function(t){return 2*t+1},t.prototype.getRightChildIndex_=function(t){return 2*t+2},t.prototype.getParentIndex_=function(t){return t-1>>1},t.prototype.heapify_=function(){var t;for(t=(this.elements_.length>>1)-1;t>=0;t--)this.siftUp_(t)},t.prototype.isEmpty=function(){return 0===this.elements_.length},t.prototype.isKeyQueued=function(t){return t in this.queuedElements_},t.prototype.isQueued=function(t){return this.isKeyQueued(this.keyFunction_(t))},t.prototype.siftUp_=function(t){for(var e=this.elements_,r=this.priorities_,n=e.length,i=e[t],o=r[t],a=t;t>1;){var s=this.getLeftChildIndex_(t),u=this.getRightChildIndex_(t),l=ut;){var a=this.getParentIndex_(e);if(!(n[a]>o))break;r[e]=r[a],n[e]=n[a],e=a}r[e]=i,n[e]=o},t.prototype.reprioritize=function(){var t,e,r,n=this.priorityFunction_,i=this.elements_,o=this.priorities_,a=0,s=i.length;for(e=0;e0;)i=(n=this.dequeue()[0]).getKey(),(r=n.getState())===wn.ABORT?a=!0:r!==wn.IDLE||i in this.tilesLoadingKeys_||(this.tilesLoadingKeys_[i]=!0,++this.tilesLoading_,++o,n.load());0===o&&a&&this.tileChangeCallback_()},e}(xn),Cn=42,Pn=256;function In(t,e,r){return function(n,i,o,a){if(n){var s=e?0:o[0]*i,u=e?0:o[1]*i,l=t[0]+s/2,h=t[2]-s/2,c=t[1]+u/2,p=t[3]-u/2;l>h&&(h=l=(h+l)/2),c>p&&(p=c=(p+c)/2);var f=kt(n[0],l,h),d=kt(n[1],c,p),_=30*i;return a&&r&&(f+=-_*Math.log(1+Math.max(0,l-n[0])/_)+_*Math.log(1+Math.max(0,n[0]-h)/_),d+=-_*Math.log(1+Math.max(0,c-n[1])/_)+_*Math.log(1+Math.max(0,n[1]-p)/_)),[f,d]}}}function bn(t){return t}function Ln(t,e,r){var n=It(e)/r[0],i=Ot(e)/r[1];return Math.min(t,Math.min(n,i))}function Mn(t,e,r){var n=Math.min(t,e);return n*=Math.log(1+50*Math.max(0,t/e-1))/50+1,r&&(n=Math.max(n,r),n/=Math.log(1+50*Math.max(0,r/t-1))/50+1),kt(n,r/2,2*e)}function Fn(t,e,r,n){return function(i,o,a,s){if(void 0!==i){var u=n?Ln(t,n,a):t;return(void 0===r||r)&&s?Mn(i,u,e):kt(i,e,u)}}}function An(t){return void 0!==t?0:void 0}function Nn(t){return void 0!==t?t:void 0}var Dn=0,Gn=1,jn="center",kn="resolution",Un="rotation";function Bn(t,e,r){var n=void 0!==r?t.toFixed(r):""+t,i=n.indexOf(".");return(i=-1===i?n.length:i)>e?n:new Array(1+e-i).join("0")+n}function Yn(t,e){for(var r=(""+t).split("."),n=(""+e).split("."),i=0;ia)return 1;if(a>o)return-1}return 0}function zn(t,e){return t[0]+=+e[0],t[1]+=+e[1],t}function Xn(t,e){var r,n,i=t[0],o=t[1],a=e[0],s=e[1],u=a[0],l=a[1],h=s[0],c=s[1],p=h-u,f=c-l,d=0===p&&0===f?0:(p*(i-u)+f*(o-l))/(p*p+f*f||0);return d<=0?(r=u,n=l):d>=1?(r=h,n=c):(r=u+d*p,n=l+d*f),[r,n]}function Vn(t,e,r){var n=Vt(e+180,360)-180,i=Math.abs(3600*n),o=r||0,a=Math.pow(10,o),s=Math.floor(i/3600),u=Math.floor((i-3600*s)/60),l=i-3600*s-60*u;return(l=Math.ceil(l*a)/a)>=60&&(l=0,u+=1),u>=60&&(u=0,s+=1),s+"° "+Bn(u,2)+"′ "+Bn(l,2,o)+"″"+(0==n?"":" "+t.charAt(n<0?1:0))}function Wn(t,e,r){return t?e.replace("{x}",t[0].toFixed(r)).replace("{y}",t[1].toFixed(r)):""}function Zn(t,e){for(var r=!0,n=t.length-1;n>=0;--n)if(t[n]!=e[n]){r=!1;break}return r}function Kn(t,e){var r=Math.cos(e),n=Math.sin(e),i=t[0]*r-t[1]*n,o=t[1]*r+t[0]*n;return t[0]=i,t[1]=o,t}function Hn(t,e){return t[0]*=e,t[1]*=e,t}function qn(t,e){var r=t[0]-e[0],n=t[1]-e[1];return r*r+n*n}function Jn(t,e){return Math.sqrt(qn(t,e))}function Qn(t,e){return qn(t,Xn(t,e))}function $n(t,e){return Wn(t,"{x}, {y}",e)}function ti(t){return Math.pow(t,3)}function ei(t){return 1-ti(1-t)}function ri(t){return 3*t*t-2*t*t*t}function ni(t){return t}var ii=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),oi=0;function ai(t,e){setTimeout(function(){t(e)},0)}function si(t){return!(t.sourceCenter&&t.targetCenter&&!Zn(t.sourceCenter,t.targetCenter))&&(t.sourceResolution===t.targetResolution&&t.sourceRotation===t.targetRotation)}var ui=function(t){function e(e){var r=t.call(this)||this,n=p({},e);return r.hints_=[0,0],r.animations_=[],r.updateAnimationKey_,r.projection_=Re(n.projection,"EPSG:3857"),r.targetCenter_=null,r.targetResolution_,r.targetRotation_,n.center&&(n.center=Ue(n.center,r.projection_)),n.extent&&(n.extent=Ye(n.extent,r.projection_)),r.applyOptions_(n),r}return ii(e,t),e.prototype.applyOptions_=function(t){var e=function(t){var e,r,n,i=void 0!==t.minZoom?t.minZoom:oi,o=void 0!==t.maxZoom?t.maxZoom:28,a=void 0!==t.zoomFactor?t.zoomFactor:2,s=void 0!==t.multiWorld&&t.multiWorld,u=void 0===t.smoothResolutionConstraint||t.smoothResolutionConstraint,l=Re(t.projection,"EPSG:3857"),h=l.getExtent(),c=t.constrainOnlyCenter,p=t.extent;s||p||!l.isGlobal()||(c=!1,p=h);if(void 0!==t.resolutions){var f=t.resolutions;r=f[i],n=void 0!==f[o]?f[o]:f[f.length-1],e=t.constrainResolution?function(t,e,r){return function(n,i,o,a){if(void 0!==n){var s=t[0],u=t[t.length-1],l=r?Ln(s,r,o):s;if(a)return void 0===e||e?Mn(n,l,u):kt(n,u,l);var h=Math.min(l,n),c=Math.floor(S(t,h,i));return t[c]>l&&c1&&"function"==typeof arguments[r-1]&&(e=arguments[r-1],--r),!this.isDef()){var n=arguments[r-1];return n.center&&this.setCenterInternal(n.center),void 0!==n.zoom&&this.setZoom(n.zoom),void 0!==n.rotation&&this.setRotation(n.rotation),void(e&&ai(e,!0))}for(var i=Date.now(),o=this.targetCenter_.slice(),a=this.targetResolution_,s=this.targetRotation_,u=[],l=0;l0},e.prototype.getInteracting=function(){return this.hints_[Gn]>0},e.prototype.cancelAnimations=function(){this.setHint(Dn,-this.hints_[Dn]);for(var t=0,e=this.animations_.length;t=0;--r){for(var n=this.animations_[r],i=!0,o=0,a=n.length;o0?u/s.duration:1;l>=1?(s.complete=!0,l=1):i=!1;var h=s.easing(l);if(s.sourceCenter){var c=s.sourceCenter[0],p=s.sourceCenter[1],f=c+h*(s.targetCenter[0]-c),d=p+h*(s.targetCenter[1]-p);this.targetCenter_=[f,d]}if(s.sourceResolution&&s.targetResolution){var _=1===h?s.targetResolution:s.sourceResolution+h*(s.targetResolution-s.sourceResolution);if(s.anchor){var g=this.getSizeFromViewport_(this.getRotation()),y=this.constraints_.resolution(_,0,g,!0);this.targetCenter_=this.calculateCenterZoom(y,s.anchor)}this.targetResolution_=_,this.applyTargetState_(!0)}if(void 0!==s.sourceRotation&&void 0!==s.targetRotation){var v=1===h?Vt(s.targetRotation+Math.PI,2*Math.PI)-Math.PI:s.sourceRotation+h*(s.targetRotation-s.sourceRotation);if(s.anchor){var m=this.constraints_.rotation(v,!0);this.targetCenter_=this.calculateCenterRotate(m,s.anchor)}this.targetRotation_=v}if(this.applyTargetState_(!0),e=!0,!s.complete)break}}if(i){this.animations_[r]=null,this.setHint(Dn,-1);var E=n[0].callback;E&&ai(E,!0)}}this.animations_=this.animations_.filter(Boolean),e&&void 0===this.updateAnimationKey_&&(this.updateAnimationKey_=requestAnimationFrame(this.updateAnimations_.bind(this)))}},e.prototype.calculateCenterRotate=function(t,e){var r,n=this.getCenterInternal();return void 0!==n&&(Kn(r=[n[0]-e[0],n[1]-e[1]],t-this.getRotation()),zn(r,e)),r},e.prototype.calculateCenterZoom=function(t,e){var r,n=this.getCenterInternal(),i=this.getResolution();void 0!==n&&void 0!==i&&(r=[e[0]-t*(e[0]-n[0])/i,e[1]-t*(e[1]-n[1])/i]);return r},e.prototype.getSizeFromViewport_=function(t){var e=[100,100],r='.ol-viewport[data-view="'+o(this)+'"]',n=document.querySelector(r);if(n){var i=getComputedStyle(n);e[0]=parseInt(i.width,10),e[1]=parseInt(i.height,10)}if(t){var a=e[0],s=e[1];e[0]=Math.abs(a*Math.cos(t))+Math.abs(s*Math.sin(t)),e[1]=Math.abs(a*Math.sin(t))+Math.abs(s*Math.cos(t))}return e},e.prototype.getCenter=function(){var t=this.getCenterInternal();return t?ke(t,this.getProjection()):t},e.prototype.getCenterInternal=function(){return this.get(jn)},e.prototype.getConstraints=function(){return this.constraints_},e.prototype.getHints=function(t){return void 0!==t?(t[0]=this.hints_[0],t[1]=this.hints_[1],t):this.hints_.slice()},e.prototype.calculateExtent=function(t){return Be(this.calculateExtentInternal(t),this.getProjection())},e.prototype.calculateExtentInternal=function(t){var e=t||this.getSizeFromViewport_(),r=this.getCenterInternal();K(r,1);var n=this.getResolution();K(void 0!==n,2);var i=this.getRotation();return K(void 0!==i,3),xt(r,n,i,e)},e.prototype.getMaxResolution=function(){return this.maxResolution_},e.prototype.getMinResolution=function(){return this.minResolution_},e.prototype.getMaxZoom=function(){return this.getZoomForResolution(this.minResolution_)},e.prototype.setMaxZoom=function(t){this.applyOptions_(this.getUpdatedOptions_({maxZoom:t}))},e.prototype.getMinZoom=function(){return this.getZoomForResolution(this.maxResolution_)},e.prototype.setMinZoom=function(t){this.applyOptions_(this.getUpdatedOptions_({minZoom:t}))},e.prototype.setConstrainResolution=function(t){this.applyOptions_(this.getUpdatedOptions_({constrainResolution:t}))},e.prototype.getProjection=function(){return this.projection_},e.prototype.getResolution=function(){return this.get(kn)},e.prototype.getResolutions=function(){return this.resolutions_},e.prototype.getResolutionForExtent=function(t,e){return this.getResolutionForExtentInternal(Ye(t,this.getProjection()),e)},e.prototype.getResolutionForExtentInternal=function(t,e){var r=e||this.getSizeFromViewport_(),n=It(t)/r[0],i=Ot(t)/r[1];return Math.max(n,i)},e.prototype.getResolutionForValueFunction=function(t){var e=t||2,r=this.maxResolution_,n=this.minResolution_,i=Math.log(r/n)/Math.log(e);return function(t){return r/Math.pow(e,t*i)}},e.prototype.getRotation=function(){return this.get(Un)},e.prototype.getValueForResolutionFunction=function(t){var e=t||2,r=this.maxResolution_,n=this.minResolution_,i=Math.log(r/n)/Math.log(e);return function(t){return Math.log(r/t)/Math.log(e)/i}},e.prototype.getState=function(){var t=this.getCenterInternal(),e=this.getProjection(),r=this.getResolution(),n=this.getRotation();return{center:t.slice(0),projection:void 0!==e?e:null,resolution:r,rotation:n,zoom:this.getZoom()}},e.prototype.getZoom=function(){var t,e=this.getResolution();return void 0!==e&&(t=this.getZoomForResolution(e)),t},e.prototype.getZoomForResolution=function(t){var e,r,n=this.minZoom_||0;if(this.resolutions_){var i=S(this.resolutions_,t,1);n=i,e=this.resolutions_[i],r=i==this.resolutions_.length-1?2:e/this.resolutions_[i+1]}else e=this.maxResolution_,r=this.zoomFactor_;return n+Math.log(e/t)/Math.log(r)},e.prototype.getResolutionForZoom=function(t){if(this.resolutions_){if(this.resolutions_.length<=1)return 0;var e=kt(Math.floor(t),0,this.resolutions_.length-2),r=this.resolutions_[e]/this.resolutions_[e+1];return this.resolutions_[e]/Math.pow(r,kt(t-e,0,1))}return this.maxResolution_/Math.pow(this.zoomFactor_,t-this.minZoom_)},e.prototype.fit=function(t,e){var r,n=p({size:this.getSizeFromViewport_()},e||{});if(K(Array.isArray(t)||"function"==typeof t.getSimplifiedGeometry,24),Array.isArray(t))K(!Lt(t),25),r=zr(i=Ye(t,this.getProjection()));else if(t.getType()===Nt.CIRCLE){var i;(r=zr(i=Ye(t.getExtent(),this.getProjection()))).rotate(this.getRotation(),St(i))}else{var o=je();r=o?r.clone().transform(o,this.getProjection()):t}this.fitInternal(r,n)},e.prototype.fitInternal=function(t,e){var r=e||{},n=r.size;n||(n=this.getSizeFromViewport_());var i,o=void 0!==r.padding?r.padding:[0,0,0,0],a=void 0!==r.nearest&&r.nearest;i=void 0!==r.minResolution?r.minResolution:void 0!==r.maxZoom?this.getResolutionForZoom(r.maxZoom):0;for(var s=t.getFlatCoordinates(),u=this.getRotation(),l=Math.cos(-u),h=Math.sin(-u),c=1/0,p=1/0,f=-1/0,d=-1/0,_=t.getStride(),g=0,y=s.length;g=0;n--){var i=r[n];if(i.getActive())if(!i.handleEvent(t))break}}},e.prototype.handlePostRender=function(){var t=this.frameState_,e=this.tileQueue_;if(!e.isEmpty()){var r=this.maxTilesLoading_,n=r;if(t){var i=t.viewHints;if(i[Dn]||i[Gn]){var o=!sn&&Date.now()-t.time>8;r=o?0:8,n=o?0:2}}e.getTilesLoading()0&&t[1]>0}(e)&&r&&r.isDef()){var o=r.getHints(this.frameState_?this.frameState_.viewHints:void 0),a=r.getState();i={animate:!1,coordinateToPixelTransform:this.coordinateToPixelTransform_,declutterItems:n?n.declutterItems:[],extent:xt(a.center,a.resolution,a.rotation,e),index:this.frameIndex_++,layerIndex:0,layerStatesArray:this.getLayerGroup().getLayerStatesArray(),pixelRatio:this.pixelRatio_,pixelToCoordinateTransform:this.pixelToCoordinateTransform_,postRenderFunctions:[],size:e,tileQueue:this.tileQueue_,time:t,usedTiles:{},viewState:a,viewHints:o,wantedTiles:{}}}if(this.frameState_=i,this.renderer_.renderFrame(i),i){if(i.animate&&this.render(),Array.prototype.push.apply(this.postRenderFunctions_,i.postRenderFunctions),n)(!this.previousExtent_||!Lt(this.previousExtent_)&&!pt(i.extent,this.previousExtent_))&&(this.dispatchEvent(new Qr(_n,this,n)),this.previousExtent_=lt(this.previousExtent_));this.previousExtent_&&!i.viewHints[Dn]&&!i.viewHints[Gn]&&!pt(i.extent,this.previousExtent_)&&(this.dispatchEvent(new Qr(gn,this,i)),et(i.extent,this.previousExtent_))}this.dispatchEvent(new Qr(dn,this,i)),this.postRenderTimeoutHandle_=setTimeout(this.handlePostRender.bind(this),0)},e.prototype.setLayerGroup=function(t){this.set(yn.LAYERGROUP,t)},e.prototype.setSize=function(t){this.set(yn.SIZE,t)},e.prototype.setTarget=function(t){this.set(yn.TARGET,t)},e.prototype.setView=function(t){this.set(yn.VIEW,t)},e.prototype.updateSize=function(){var t=this.getTargetElement();if(t){var e=getComputedStyle(t);this.setSize([t.offsetWidth-parseFloat(e.borderLeftWidth)-parseFloat(e.paddingLeft)-parseFloat(e.paddingRight)-parseFloat(e.borderRightWidth),t.offsetHeight-parseFloat(e.borderTopWidth)-parseFloat(e.paddingTop)-parseFloat(e.paddingBottom)-parseFloat(e.borderBottomWidth)])}else this.setSize(void 0)},e}(z),bi=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Li=function(t){function e(e){var r=t.call(this)||this;return r.element=e.element?e.element:null,r.target_=null,r.map_=null,r.listenerKeys=[],r.render=e.render?e.render:b,e.target&&r.setTarget(e.target),r}return bi(e,t),e.prototype.disposeInternal=function(){fi(this.element),t.prototype.disposeInternal.call(this)},e.prototype.getMap=function(){return this.map_},e.prototype.setMap=function(t){this.map_&&fi(this.element);for(var e=0,r=this.listenerKeys.length;e=t.maxResolution)return!1;var n=e.zoom;return n>t.minZoom&&n<=t.maxZoom}var Bi=function(t){function e(e){var r=this,n=p({},e);delete n.source,(r=t.call(this,n)||this).mapPrecomposeKey_=null,r.mapRenderKey_=null,r.sourceChangeKey_=null,r.renderer_=null,e.render&&(r.render=e.render),e.map&&r.setMap(e.map),r.addEventListener(Y(_i.SOURCE),r.handleSourcePropertyChange_);var i=e.source?e.source:null;return r.setSource(i),r}return ki(e,t),e.prototype.getLayersArray=function(t){var e=t||[];return e.push(this),e},e.prototype.getLayerStatesArray=function(t){var e=t||[];return e.push(this.getLayerState()),e},e.prototype.getSource=function(){return this.get(_i.SOURCE)||null},e.prototype.getSourceState=function(){var t=this.getSource();return t?t.getState():vi.UNDEFINED},e.prototype.handleSourceChange_=function(){this.changed()},e.prototype.handleSourcePropertyChange_=function(){this.sourceChangeKey_&&(v(this.sourceChangeKey_),this.sourceChangeKey_=null);var t=this.getSource();t&&(this.sourceChangeKey_=g(t,N.CHANGE,this.handleSourceChange_,this)),this.changed()},e.prototype.getFeatures=function(t){return this.renderer_.getFeatures(t)},e.prototype.render=function(t,e){var r=this.getRenderer();if(r.prepareFrame(t))return r.renderFrame(t,e)},e.prototype.setMap=function(t){this.mapPrecomposeKey_&&(v(this.mapPrecomposeKey_),this.mapPrecomposeKey_=null),t||this.changed(),this.mapRenderKey_&&(v(this.mapRenderKey_),this.mapRenderKey_=null),t&&(this.mapPrecomposeKey_=g(t,En,function(t){t.frameState.layerStatesArray.push(this.getLayerState(!1))},this),this.mapRenderKey_=g(this,N.CHANGE,t.render,t),this.changed())},e.prototype.setSource=function(t){this.set(_i.SOURCE,t)},e.prototype.getRenderer=function(){return this.renderer_||(this.renderer_=this.createRenderer()),this.renderer_},e.prototype.hasRenderer=function(){return!!this.renderer_},e.prototype.createRenderer=function(){return null},e.prototype.disposeInternal=function(){this.setSource(null),t.prototype.disposeInternal.call(this)},e}(yi),Yi=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function zi(t){this.updateElement_(t.frameState)}var Xi=function(t){function e(e){var r=this,n=e||{};(r=t.call(this,{element:document.createElement("div"),render:n.render||zi,target:n.target})||this).ulElement_=document.createElement("ul"),r.collapsed_=void 0===n.collapsed||n.collapsed,r.overrideCollapsible_=void 0!==n.collapsible,r.collapsible_=void 0===n.collapsible||n.collapsible,r.collapsible_||(r.collapsed_=!1);var i=void 0!==n.className?n.className:"ol-attribution",o=void 0!==n.tipLabel?n.tipLabel:"Attributions",a=void 0!==n.collapseLabel?n.collapseLabel:"»";"string"==typeof a?(r.collapseLabel_=document.createElement("span"),r.collapseLabel_.textContent=a):r.collapseLabel_=a;var s=void 0!==n.label?n.label:"i";"string"==typeof s?(r.label_=document.createElement("span"),r.label_.textContent=s):r.label_=s;var u=r.collapsible_&&!r.collapsed_?r.collapseLabel_:r.label_,l=document.createElement("button");l.setAttribute("type","button"),l.title=o,l.appendChild(u),l.addEventListener(N.CLICK,r.handleClick_.bind(r),!1);var h=i+" "+Ai+" "+Di+(r.collapsed_&&r.collapsible_?" "+Gi:"")+(r.collapsible_?"":" ol-uncollapsible"),c=r.element;return c.className=h,c.appendChild(r.ulElement_),c.appendChild(l),r.renderedAttributions_=[],r.renderedVisible_=!0,r}return Yi(e,t),e.prototype.collectSourceAttributions_=function(t){for(var e={},r=[],n=t.layerStatesArray,i=0,o=n.length;i0;if(this.renderedVisible_!=r&&(this.element.style.display=r?"":"none",this.renderedVisible_=r),!R(e,this.renderedAttributions_)){di(this.ulElement_);for(var n=0,i=e.length;n0?t.animate({rotation:0,duration:this.duration_,easing:ei}):t.setRotation(0))},e}(Li),Ki=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Hi=function(t){function e(e){var r=this,n=e||{};r=t.call(this,{element:document.createElement("div"),target:n.target})||this;var i=void 0!==n.className?n.className:"ol-zoom",o=void 0!==n.delta?n.delta:1,a=void 0!==n.zoomInLabel?n.zoomInLabel:"+",s=void 0!==n.zoomOutLabel?n.zoomOutLabel:"−",u=void 0!==n.zoomInTipLabel?n.zoomInTipLabel:"Zoom in",l=void 0!==n.zoomOutTipLabel?n.zoomOutTipLabel:"Zoom out",h=document.createElement("button");h.className=i+"-in",h.setAttribute("type","button"),h.title=u,h.appendChild("string"==typeof a?document.createTextNode(a):a),h.addEventListener(N.CLICK,r.handleClick_.bind(r,o),!1);var c=document.createElement("button");c.className=i+"-out",c.setAttribute("type","button"),c.title=l,c.appendChild("string"==typeof s?document.createTextNode(s):s),c.addEventListener(N.CLICK,r.handleClick_.bind(r,-o),!1);var p=i+" "+Ai+" "+Di,f=r.element;return f.className=p,f.appendChild(h),f.appendChild(c),r.duration_=void 0!==n.duration?n.duration:250,r}return Ki(e,t),e.prototype.handleClick_=function(t,e){e.preventDefault(),this.zoomByDelta_(t)},e.prototype.zoomByDelta_=function(t){var e=this.getMap().getView();if(e){var r=e.getZoom();if(void 0!==r){var n=e.getConstrainedZoom(r+t);this.duration_>0?(e.getAnimating()&&e.cancelAnimations(),e.animate({zoom:n,duration:this.duration_,easing:ei})):e.setZoom(n)}}},e}(Li);function qi(t){var e=t||{},r=new Z;return(void 0===e.zoom||e.zoom)&&r.push(new Hi(e.zoomOptions)),(void 0===e.rotate||e.rotate)&&r.push(new Zi(e.rotateOptions)),(void 0===e.attribution||e.attribution)&&r.push(new Xi(e.attributionOptions)),r}var Ji={ACTIVE:"active"},Qi=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function $i(t,e,r,n){var i=t.getZoom();if(void 0!==i){var o=t.getConstrainedZoom(i+e),a=t.getResolutionForZoom(o);t.getAnimating()&&t.cancelAnimations(),t.animate({resolution:a,anchor:r,duration:void 0!==n?n:250,easing:ei})}}var to=function(t){function e(e){var r=t.call(this)||this;return e.handleEvent&&(r.handleEvent=e.handleEvent),r.map_=null,r.setActive(!0),r}return Qi(e,t),e.prototype.getActive=function(){return this.get(Ji.ACTIVE)},e.prototype.getMap=function(){return this.map_},e.prototype.handleEvent=function(t){return!0},e.prototype.setActive=function(t){this.set(Ji.ACTIVE,t)},e.prototype.setMap=function(t){this.map_=t},e}(z),eo=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function ro(t){var e=!1;if(t.type==un.DBLCLICK){var r=t.originalEvent,n=t.map,i=t.coordinate,o=r.shiftKey?-this.delta_:this.delta_;$i(n.getView(),o,i,this.duration_),t.preventDefault(),e=!0}return!e}var no=function(t){function e(e){var r=t.call(this,{handleEvent:ro})||this,n=e||{};return r.delta_=n.delta?n.delta:1,r.duration_=void 0!==n.duration?n.duration:250,r}return eo(e,t),e}(to),io=function(t){var e=t.originalEvent;return e.altKey&&!(e.metaKey||e.ctrlKey)&&!e.shiftKey},oo=function(t){var e=t.originalEvent;return e.altKey&&!(e.metaKey||e.ctrlKey)&&e.shiftKey},ao=function(t){return t.target.getTargetElement()===document.activeElement},so=P,uo=function(t){var e=t.originalEvent;return 0==e.button&&!(nn&&on&&e.ctrlKey)},lo=I,ho=function(t){return t.type==un.SINGLECLICK},co=function(t){var e=t.originalEvent;return!e.altKey&&!(e.metaKey||e.ctrlKey)&&!e.shiftKey},po=function(t){var e=t.originalEvent;return!e.altKey&&!(e.metaKey||e.ctrlKey)&&e.shiftKey},fo=function(t){var e=t.target.tagName;return"INPUT"!==e&&"SELECT"!==e&&"TEXTAREA"!==e},_o=function(t){var e=t.pointerEvent;return K(void 0!==e,56),"mouse"==e.pointerType},go=function(t){var e=t.pointerEvent;return K(void 0!==e,56),e.isPrimary&&0===e.button},yo=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function vo(t){for(var e=t.length,r=0,n=0,i=0;i0}}else if(t.type==un.POINTERDOWN){var n=this.handleDownEvent(t);n&&t.preventDefault(),this.handlingDownUpSequence=n,e=this.stopDown(n)}else t.type==un.POINTERMOVE&&this.handleMoveEvent(t);return!e},e.prototype.handleMoveEvent=function(t){},e.prototype.handleUpEvent=function(t){return!1},e.prototype.stopDown=function(t){return t},e.prototype.updateTrackedPointers_=function(t){if(function(t){var e=t.type;return e===un.POINTERDOWN||e===un.POINTERDRAG||e===un.POINTERUP}(t)){var e=t.pointerEvent,r=e.pointerId.toString();t.type==un.POINTERUP?delete this.trackedPointers_[r]:t.type==un.POINTERDOWN?this.trackedPointers_[r]=e:r in this.trackedPointers_&&(this.trackedPointers_[r]=e),this.targetPointers=d(this.trackedPointers_)}},e}(to),Eo=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function To(t){return co(t)&&go(t)}var So=function(t){function e(e){var r=t.call(this,{stopDown:I})||this,n=e||{};return r.kinetic_=n.kinetic,r.lastCentroid=null,r.lastPointersCount_,r.panning_=!1,r.condition_=n.condition?n.condition:To,r.noKinetic_=!1,r}return Eo(e,t),e.prototype.handleDragEvent=function(t){this.panning_||(this.panning_=!0,this.getMap().getView().beginInteraction());var e=this.targetPointers,r=vo(e);if(e.length==this.lastPointersCount_){if(this.kinetic_&&this.kinetic_.update(r[0],r[1]),this.lastCentroid){var n=[this.lastCentroid[0]-r[0],r[1]-this.lastCentroid[1]],i=t.map.getView();Hn(n,i.getResolution()),Kn(n,i.getRotation()),i.adjustCenterInternal(n)}}else this.kinetic_&&this.kinetic_.begin();this.lastCentroid=r,this.lastPointersCount_=e.length},e.prototype.handleUpEvent=function(t){var e=t.map,r=e.getView();if(0===this.targetPointers.length){if(!this.noKinetic_&&this.kinetic_&&this.kinetic_.end()){var n=this.kinetic_.getDistance(),i=this.kinetic_.getAngle(),o=r.getCenterInternal(),a=e.getPixelFromCoordinateInternal(o),s=e.getCoordinateFromPixelInternal([a[0]-n*Math.cos(i),a[1]-n*Math.sin(i)]);r.animateInternal({center:r.getConstrainedCenter(s),duration:500,easing:ei})}return this.panning_&&(this.panning_=!1,r.endInteraction()),!1}return this.kinetic_&&this.kinetic_.begin(),this.lastCentroid=null,!0},e.prototype.handleDownEvent=function(t){if(this.targetPointers.length>0&&this.condition_(t)){var e=t.map.getView();return this.lastCentroid=null,e.getAnimating()&&e.cancelAnimations(),this.kinetic_&&this.kinetic_.begin(),this.noKinetic_=this.targetPointers.length>1,!0}return!1},e}(mo),wo=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),xo=function(t){function e(e){var r=this,n=e||{};return(r=t.call(this,{stopDown:I})||this).condition_=n.condition?n.condition:oo,r.lastAngle_=void 0,r.duration_=void 0!==n.duration?n.duration:250,r}return wo(e,t),e.prototype.handleDragEvent=function(t){if(_o(t)){var e=t.map,r=e.getView();if(r.getConstraints().rotation!==An){var n=e.getSize(),i=t.pixel,o=Math.atan2(n[1]/2-i[1],i[0]-n[0]/2);if(void 0!==this.lastAngle_){var a=o-this.lastAngle_;r.adjustRotationInternal(-a)}this.lastAngle_=o}}},e.prototype.handleUpEvent=function(t){return!_o(t)||(t.map.getView().endInteraction(this.duration_),!1)},e.prototype.handleDownEvent=function(t){return!!_o(t)&&(!(!uo(t)||!this.condition_(t))&&(t.map.getView().beginInteraction(),this.lastAngle_=void 0,!0))},e}(mo),Oo=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Ro=function(t){function e(e){var r=t.call(this)||this;return r.geometry_=null,r.element_=document.createElement("div"),r.element_.style.position="absolute",r.element_.className="ol-box "+e,r.map_=null,r.startPixel_=null,r.endPixel_=null,r}return Oo(e,t),e.prototype.disposeInternal=function(){this.setMap(null)},e.prototype.render_=function(){var t=this.startPixel_,e=this.endPixel_,r=this.element_.style;r.left=Math.min(t[0],e[0])+"px",r.top=Math.min(t[1],e[1])+"px",r.width=Math.abs(e[0]-t[0])+"px",r.height=Math.abs(e[1]-t[1])+"px"},e.prototype.setMap=function(t){if(this.map_){this.map_.getOverlayContainer().removeChild(this.element_);var e=this.element_.style;e.left="inherit",e.top="inherit",e.width="inherit",e.height="inherit"}this.map_=t,this.map_&&this.map_.getOverlayContainer().appendChild(this.element_)},e.prototype.setPixels=function(t,e){this.startPixel_=t,this.endPixel_=e,this.createOrUpdateGeometry(),this.render_()},e.prototype.createOrUpdateGeometry=function(){var t=this.startPixel_,e=this.endPixel_,r=[t,[t[0],e[1]],e,[e[0],t[1]]].map(this.map_.getCoordinateFromPixelInternal,this.map_);r[4]=r[0].slice(),this.geometry_?this.geometry_.setCoordinates([r]):this.geometry_=new Br([r])},e.prototype.getGeometry=function(){return this.geometry_},e}(m),Co=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Po="boxstart",Io="boxdrag",bo="boxend",Lo=function(t){function e(e,r,n){var i=t.call(this,e)||this;return i.coordinate=r,i.mapBrowserEvent=n,i}return Co(e,t),e}(M),Mo=function(t){function e(e){var r=t.call(this)||this,n=e||{};return r.box_=new Ro(n.className||"ol-dragbox"),r.minArea_=void 0!==n.minArea?n.minArea:64,r.onBoxEnd_=n.onBoxEnd?n.onBoxEnd:b,r.startPixel_=null,r.condition_=n.condition?n.condition:so,r.boxEndCondition_=n.boxEndCondition?n.boxEndCondition:r.defaultBoxEndCondition,r}return Co(e,t),e.prototype.defaultBoxEndCondition=function(t,e,r){var n=r[0]-e[0],i=r[1]-e[1];return n*n+i*i>=this.minArea_},e.prototype.getGeometry=function(){return this.box_.getGeometry()},e.prototype.handleDragEvent=function(t){_o(t)&&(this.box_.setPixels(this.startPixel_,t.pixel),this.dispatchEvent(new Lo(Io,t.coordinate,t)))},e.prototype.handleUpEvent=function(t){return!_o(t)||(this.box_.setMap(null),this.boxEndCondition_(t,this.startPixel_,t.pixel)&&(this.onBoxEnd_(t),this.dispatchEvent(new Lo(bo,t.coordinate,t))),!1)},e.prototype.handleDownEvent=function(t){return!!_o(t)&&(!(!uo(t)||!this.condition_(t))&&(this.startPixel_=t.pixel,this.box_.setMap(t.map),this.box_.setPixels(this.startPixel_,this.startPixel_),this.dispatchEvent(new Lo(Po,t.coordinate,t)),!0))},e}(mo),Fo=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function Ao(){var t,e,r=this.getMap(),n=r.getView(),i=r.getSize(),o=this.getGeometry().getExtent();if(this.out_){var a=n.calculateExtentInternal(i),s=(t=[r.getPixelFromCoordinateInternal(Et(o)),r.getPixelFromCoordinateInternal(Pt(o))],_t(lt(e),t));Mt(a,1/n.getResolutionForExtentInternal(s,i)),o=a}var u=n.getConstrainedResolution(n.getResolutionForExtentInternal(o,i)),l=n.getConstrainedCenter(St(o),u);n.animateInternal({resolution:u,center:l,duration:this.duration_,easing:ei})}var No=function(t){function e(e){var r=this,n=e||{},i=n.condition?n.condition:po;return(r=t.call(this,{condition:i,className:n.className||"ol-dragzoom",minArea:n.minArea,onBoxEnd:Ao})||this).duration_=void 0!==n.duration?n.duration:200,r.out_=void 0!==n.out&&n.out,r}return Fo(e,t),e}(Mo),Do={LEFT:37,UP:38,RIGHT:39,DOWN:40},Go=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function jo(t){var e=!1;if(t.type==N.KEYDOWN){var r=t.originalEvent.keyCode;if(this.condition_(t)&&(r==Do.DOWN||r==Do.LEFT||r==Do.RIGHT||r==Do.UP)){var n=t.map.getView(),i=n.getResolution()*this.pixelDelta_,o=0,a=0;r==Do.DOWN?a=-i:r==Do.LEFT?o=-i:r==Do.RIGHT?o=i:a=i;var s=[o,a];Kn(s,n.getRotation()),function(t,e,r){var n=t.getCenterInternal();if(n){var i=[n[0]+e[0],n[1]+e[1]];t.animateInternal({duration:void 0!==r?r:250,easing:ni,center:t.getConstrainedCenter(i)})}}(n,s,this.duration_),t.preventDefault(),e=!0}}return!e}var ko=function(t){function e(e){var r=t.call(this,{handleEvent:jo})||this,n=e||{};return r.defaultCondition_=function(t){return co(t)&&fo(t)},r.condition_=void 0!==n.condition?n.condition:r.defaultCondition_,r.duration_=void 0!==n.duration?n.duration:100,r.pixelDelta_=void 0!==n.pixelDelta?n.pixelDelta:128,r}return Go(e,t),e}(to),Uo=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function Bo(t){var e=!1;if(t.type==N.KEYDOWN||t.type==N.KEYPRESS){var r=t.originalEvent.charCode;if(this.condition_(t)&&(r=="+".charCodeAt(0)||r=="-".charCodeAt(0))){var n=t.map,i=r=="+".charCodeAt(0)?this.delta_:-this.delta_;$i(n.getView(),i,void 0,this.duration_),t.preventDefault(),e=!0}}return!e}var Yo=function(t){function e(e){var r=t.call(this,{handleEvent:Bo})||this,n=e||{};return r.condition_=n.condition?n.condition:fo,r.delta_=n.delta?n.delta:1,r.duration_=void 0!==n.duration?n.duration:100,r}return Uo(e,t),e}(to),zo=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Xo="trackpad",Vo="wheel",Wo=function(t){function e(e){var r=this,n=e||{};return(r=t.call(this,n)||this).totalDelta_=0,r.lastDelta_=0,r.maxDelta_=void 0!==n.maxDelta?n.maxDelta:1,r.duration_=void 0!==n.duration?n.duration:250,r.timeout_=void 0!==n.timeout?n.timeout:80,r.useAnchor_=void 0===n.useAnchor||n.useAnchor,r.condition_=n.condition?n.condition:so,r.lastAnchor_=null,r.startTime_=void 0,r.timeoutId_,r.mode_=void 0,r.trackpadEventGap_=400,r.trackpadTimeoutId_,r.trackpadDeltaPerZoom_=300,r}return zo(e,t),e.prototype.endInteraction_=function(){this.trackpadTimeoutId_=void 0,this.getMap().getView().endInteraction(void 0,Math.sign(this.lastDelta_),this.lastAnchor_)},e.prototype.handleEvent=function(t){if(!this.condition_(t))return!0;if(t.type!==N.WHEEL)return!0;t.preventDefault();var e,r=t.map,n=t.originalEvent;if(this.useAnchor_&&(this.lastAnchor_=t.coordinate),t.type==N.WHEEL&&(e=n.deltaY,rn&&n.deltaMode===WheelEvent.DOM_DELTA_PIXEL&&(e/=an),n.deltaMode===WheelEvent.DOM_DELTA_LINE&&(e*=40)),0===e)return!1;this.lastDelta_=e;var i=Date.now();if(void 0===this.startTime_&&(this.startTime_=i),(!this.mode_||i-this.startTime_>this.trackpadEventGap_)&&(this.mode_=Math.abs(e)<4?Xo:Vo),this.mode_===Xo){var o=r.getView();return this.trackpadTimeoutId_?clearTimeout(this.trackpadTimeoutId_):o.beginInteraction(),this.trackpadTimeoutId_=setTimeout(this.endInteraction_.bind(this),this.trackpadEventGap_),o.adjustZoom(-e/this.trackpadDeltaPerZoom_,this.lastAnchor_),this.startTime_=i,!1}this.totalDelta_+=e;var a=Math.max(this.timeout_-(i-this.startTime_),0);return clearTimeout(this.timeoutId_),this.timeoutId_=setTimeout(this.handleWheelZoom_.bind(this,r),a),!1},e.prototype.handleWheelZoom_=function(t){var e=t.getView();e.getAnimating()&&e.cancelAnimations(),$i(e,-kt(this.totalDelta_,-this.maxDelta_,this.maxDelta_),this.lastAnchor_,this.duration_),this.mode_=void 0,this.totalDelta_=0,this.lastAnchor_=null,this.startTime_=void 0,this.timeoutId_=void 0},e.prototype.setMouseAnchor=function(t){this.useAnchor_=t,t||(this.lastAnchor_=null)},e}(to),Zo=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Ko=function(t){function e(e){var r=this,n=e||{},i=n;return i.stopDown||(i.stopDown=I),(r=t.call(this,i)||this).anchor_=null,r.lastAngle_=void 0,r.rotating_=!1,r.rotationDelta_=0,r.threshold_=void 0!==n.threshold?n.threshold:.3,r.duration_=void 0!==n.duration?n.duration:250,r}return Zo(e,t),e.prototype.handleDragEvent=function(t){var e=0,r=this.targetPointers[0],n=this.targetPointers[1],i=Math.atan2(n.clientY-r.clientY,n.clientX-r.clientX);if(void 0!==this.lastAngle_){var o=i-this.lastAngle_;this.rotationDelta_+=o,!this.rotating_&&Math.abs(this.rotationDelta_)>this.threshold_&&(this.rotating_=!0),e=o}this.lastAngle_=i;var a=t.map,s=a.getView();if(s.getConstraints().rotation!==An){var u=a.getViewport().getBoundingClientRect(),l=vo(this.targetPointers);l[0]-=u.left,l[1]-=u.top,this.anchor_=a.getCoordinateFromPixelInternal(l),this.rotating_&&(a.render(),s.adjustRotationInternal(e,this.anchor_))}},e.prototype.handleUpEvent=function(t){return!(this.targetPointers.length<2)||(t.map.getView().endInteraction(this.duration_),!1)},e.prototype.handleDownEvent=function(t){if(this.targetPointers.length>=2){var e=t.map;return this.anchor_=null,this.lastAngle_=void 0,this.rotating_=!1,this.rotationDelta_=0,this.handlingDownUpSequence||e.getView().beginInteraction(),!0}return!1},e}(mo),Ho=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),qo=function(t){function e(e){var r=this,n=e||{},i=n;return i.stopDown||(i.stopDown=I),(r=t.call(this,i)||this).anchor_=null,r.duration_=void 0!==n.duration?n.duration:400,r.lastDistance_=void 0,r.lastScaleDelta_=1,r}return Ho(e,t),e.prototype.handleDragEvent=function(t){var e=1,r=this.targetPointers[0],n=this.targetPointers[1],i=r.clientX-n.clientX,o=r.clientY-n.clientY,a=Math.sqrt(i*i+o*o);void 0!==this.lastDistance_&&(e=this.lastDistance_/a),this.lastDistance_=a;var s=t.map,u=s.getView();1!=e&&(this.lastScaleDelta_=e);var l=s.getViewport().getBoundingClientRect(),h=vo(this.targetPointers);h[0]-=l.left,h[1]-=l.top,this.anchor_=s.getCoordinateFromPixelInternal(h),s.render(),u.adjustResolutionInternal(e,this.anchor_)},e.prototype.handleUpEvent=function(t){if(this.targetPointers.length<2){var e=t.map.getView(),r=this.lastScaleDelta_>1?1:-1;return e.endInteraction(this.duration_,r),!1}return!0},e.prototype.handleDownEvent=function(t){if(this.targetPointers.length>=2){var e=t.map;return this.anchor_=null,this.lastDistance_=void 0,this.lastScaleDelta_=1,this.handlingDownUpSequence||e.getView().beginInteraction(),!0}return!1},e}(mo);function Jo(t){var e=t||{},r=new Z,n=new qr(-.005,.05,100);return(void 0===e.altShiftDragRotate||e.altShiftDragRotate)&&r.push(new xo),(void 0===e.doubleClickZoom||e.doubleClickZoom)&&r.push(new no({delta:e.zoomDelta,duration:e.zoomDuration})),(void 0===e.dragPan||e.dragPan)&&r.push(new So({condition:e.onFocusOnly?ao:void 0,kinetic:n})),(void 0===e.pinchRotate||e.pinchRotate)&&r.push(new Ko),(void 0===e.pinchZoom||e.pinchZoom)&&r.push(new qo({duration:e.zoomDuration})),(void 0===e.keyboard||e.keyboard)&&(r.push(new ko),r.push(new Yo({delta:e.zoomDelta,duration:e.zoomDuration}))),(void 0===e.mouseWheelZoom||e.mouseWheelZoom)&&r.push(new Wo({condition:e.onFocusOnly?ao:void 0,duration:e.zoomDuration})),(void 0===e.shiftDragZoom||e.shiftDragZoom)&&r.push(new No({duration:e.zoomDuration})),r}var Qo=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),$o=function(t){function e(e,r,n,i){var o=t.call(this,e)||this;return o.inversePixelTransform=r,o.frameState=n,o.context=i,o}return Qo(e,t),e}(M),ta=/^#([a-f0-9]{3}|[a-f0-9]{4}(?:[a-f0-9]{2}){0,2})$/i,ea=/^([a-z]*)$|^hsla?\(.*\)$/i;function ra(t){return"string"==typeof t?t:sa(t)}function na(t){var e=document.createElement("div");if(e.style.color=t,""!==e.style.color){document.body.appendChild(e);var r=getComputedStyle(e).color;return document.body.removeChild(e),r}return""}var ia=function(){var t={},e=0;return function(r){var n;if(t.hasOwnProperty(r))n=t[r];else{if(e>=1024){var i=0;for(var o in t)0==(3&i++)&&(delete t[o],--e)}n=function(t){var e,r,n,i,o;ea.exec(t)&&(t=na(t));if(ta.exec(t)){var a=t.length-1,s=void 0;s=a<=4?1:2;var u=4===a||8===a;e=parseInt(t.substr(1+0*s,s),16),r=parseInt(t.substr(1+1*s,s),16),n=parseInt(t.substr(1+2*s,s),16),i=u?parseInt(t.substr(1+3*s,s),16):255,1==s&&(e=(e<<4)+e,r=(r<<4)+r,n=(n<<4)+n,u&&(i=(i<<4)+i)),o=[e,r,n,i/255]}else 0==t.indexOf("rgba(")?aa(o=t.slice(5,-1).split(",").map(Number)):0==t.indexOf("rgb(")?((o=t.slice(4,-1).split(",").map(Number)).push(1),aa(o)):K(!1,14);return o}(r),t[r]=n,++e}return n}}();function oa(t){return Array.isArray(t)?t:ia(t)}function aa(t){return t[0]=kt(t[0]+.5|0,0,255),t[1]=kt(t[1]+.5|0,0,255),t[2]=kt(t[2]+.5|0,0,255),t[3]=kt(t[3],0,1),t}function sa(t){var e=t[0];e!=(0|e)&&(e=e+.5|0);var r=t[1];r!=(0|r)&&(r=r+.5|0);var n=t[2];return n!=(0|n)&&(n=n+.5|0),"rgba("+e+","+r+","+n+","+(void 0===t[3]?1:t[3])+")"}var ua=function(){function t(){this.cache_={},this.cacheSize_=0,this.maxCacheSize_=32}return t.prototype.clear=function(){this.cache_={},this.cacheSize_=0},t.prototype.canExpireCache=function(){return this.cacheSize_>this.maxCacheSize_},t.prototype.expire=function(){if(this.canExpireCache()){var t=0;for(var e in this.cache_){var r=this.cache_[e];0!=(3&t++)||r.hasListener()||(delete this.cache_[e],--this.cacheSize_)}}},t.prototype.get=function(t,e,r){var n=la(t,e,r);return n in this.cache_?this.cache_[n]:null},t.prototype.set=function(t,e,r,n){var i=la(t,e,r);this.cache_[i]=n,++this.cacheSize_},t.prototype.setSize=function(t){this.maxCacheSize_=t,this.expire()},t}();function la(t,e,r){return e+":"+t+":"+(r?ra(r):"null")}var ha=new ua;function ca(t){return Array.isArray(t)?sa(t):t}var pa,fa=function(){function t(){}return t.prototype.drawCustom=function(t,e,r){},t.prototype.drawGeometry=function(t){},t.prototype.setStyle=function(t){},t.prototype.drawCircle=function(t,e){},t.prototype.drawFeature=function(t,e){},t.prototype.drawGeometryCollection=function(t,e){},t.prototype.drawLineString=function(t,e){},t.prototype.drawMultiLineString=function(t,e){},t.prototype.drawMultiPoint=function(t,e){},t.prototype.drawMultiPolygon=function(t,e){},t.prototype.drawPoint=function(t,e){},t.prototype.drawPolygon=function(t,e){},t.prototype.drawText=function(t,e){},t.prototype.setFillStrokeStyle=function(t,e){},t.prototype.setImageStyle=function(t,e){},t.prototype.setTextStyle=function(t,e){},t}(),da=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),_a=function(t){function e(e){var r=t.call(this)||this;return r.highWaterMark=void 0!==e?e:2048,r.count_=0,r.entries_={},r.oldest_=null,r.newest_=null,r}return da(e,t),e.prototype.canExpireCache=function(){return this.getCount()>this.highWaterMark},e.prototype.clear=function(){this.count_=0,this.entries_={},this.oldest_=null,this.newest_=null,this.dispatchEvent(N.CLEAR)},e.prototype.containsKey=function(t){return this.entries_.hasOwnProperty(t)},e.prototype.forEach=function(t){for(var e=this.oldest_;e;)t(e.value_,e.key_,this),e=e.newer},e.prototype.get=function(t,e){var r=this.entries_[t];return K(void 0!==r,15),r===this.newest_?r.value_:(r===this.oldest_?(this.oldest_=this.oldest_.newer,this.oldest_.older=null):(r.newer.older=r.older,r.older.newer=r.newer),r.newer=null,r.older=this.newest_,this.newest_.newer=r,this.newest_=r,r.value_)},e.prototype.remove=function(t){var e=this.entries_[t];return K(void 0!==e,15),e===this.newest_?(this.newest_=e.older,this.newest_&&(this.newest_.newer=null)):e===this.oldest_?(this.oldest_=e.newer,this.oldest_&&(this.oldest_.older=null)):(e.newer.older=e.older,e.older.newer=e.newer),delete this.entries_[t],--this.count_,e.value_},e.prototype.getCount=function(){return this.count_},e.prototype.getKeys=function(){var t,e=new Array(this.count_),r=0;for(t=this.newest_;t;t=t.older)e[r++]=t.key_;return e},e.prototype.getValues=function(){var t,e=new Array(this.count_),r=0;for(t=this.newest_;t;t=t.older)e[r++]=t.value_;return e},e.prototype.peekLast=function(){return this.oldest_.value_},e.prototype.peekLastKey=function(){return this.oldest_.key_},e.prototype.peekFirstKey=function(){return this.newest_.key_},e.prototype.pop=function(){var t=this.oldest_;return delete this.entries_[t.key_],t.newer&&(t.newer.older=null),this.oldest_=t.newer,this.oldest_||(this.newest_=null),--this.count_,t.value_},e.prototype.replace=function(t,e){this.get(t),this.entries_[t].value_=e},e.prototype.set=function(t,e){K(!(t in this.entries_),16);var r={key_:t,newer:null,older:this.newest_,value_:e};this.newest_?this.newest_.newer=r:this.oldest_=r,this.newest_=r,this.entries_[t]=r,++this.count_},e.prototype.setSize=function(t){this.highWaterMark=t},e}(A),ga=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),ya=function(t){function e(e){var r=t.call(this,e)||this;return r.consumers={},r}return ga(e,t),e.prototype.clear=function(){this.consumers={},t.prototype.clear.call(this)},e.prototype.get=function(e,r){var n=t.prototype.get.call(this,e),i=o(r);return i in this.consumers||(this.consumers[i]={}),this.consumers[i][e]=!0,n},e.prototype.prune=function(){t:for(;this.canExpireCache();){var t=this.peekLastKey();for(var e in this.consumers)if(t in this.consumers[e])break t;var r=this.pop();for(var e in r.width=0,r.height=0,this.consumers)delete this.consumers[e][t]}},e.prototype.release=function(t){delete this.consumers[o(t)]},e}(_a),va=[],ma=[0,0,0,0],Ea=new ya,Ta={},Sa=null,wa={},xa=function(){var t,e,r=100,n=Ta,i="32px ",o=["monospace","serif"],a=o.length,s="wmytzilWMYTZIL@#/&?$%10";function u(t,r,n){for(var u=Oa(),l=!0,h=0;h=0;--n)for(var i=r[n],o=i.items,a=0,s=o.length;ad[2])p=[g+_*Math.ceil((d[0]-g)/_),t[1]];n&&f.push([-_,0],[_,0])}var y,v=e.layerStatesArray,m=v.length;this.declutterTree_&&(y=this.declutterTree_.all().map(function(t){return t.value}));for(var E=[],T=0;T=0;--S){var w=v[S],x=w.layer;if(x.hasRenderer()&&Ui(w,l)&&a.call(s,x)){var O=x.getRenderer(),R=x.getSource();if(O&&R){var C=R.getWrapX()?p:t,P=h.bind(null,w.managed);E[0]=C[0]+f[T][0],E[1]=C[1]+f[T][1],u=O.forEachFeatureAtCoordinate(E,e,r,P,y)}if(u)return u}}},e.prototype.forEachLayerAtPixel=function(t,e,r,i,o){return n()},e.prototype.hasFeatureAtCoordinate=function(t,e,r,n,i,o){return void 0!==this.forEachFeatureAtCoordinate(t,e,r,n,P,this,i,o)},e.prototype.getMap=function(){return this.map_},e.prototype.renderFrame=function(t){this.declutterTree_=Xa(t,this.declutterTree_)},e.prototype.scheduleExpireIconCache=function(t){ha.canExpireCache()&&t.postRenderFunctions.push(Wa)},e}(m),Ka=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Ha=function(t){function e(e){var r=t.call(this,e)||this;r.labelCacheKey_=g(Ea,N.CLEAR,e.redrawText.bind(e)),r.element_=document.createElement("div");var n=r.element_.style;n.position="absolute",n.width="100%",n.height="100%",n.zIndex="0",r.element_.className=Ai+" ol-layers";var i=e.getViewport();return i.insertBefore(r.element_,i.firstChild||null),r.children_=[],r.renderedVisible_=!0,r}return Ka(e,t),e.prototype.dispatchRenderEvent=function(t,e){var r=this.getMap();if(r.hasListener(t)){var n=new $o(t,void 0,e);r.dispatchEvent(n)}},e.prototype.disposeInternal=function(){v(this.labelCacheKey_),t.prototype.disposeInternal.call(this)},e.prototype.renderFrame=function(e){if(e){this.calculateMatrices2D(e),this.dispatchRenderEvent(En,e);var r=e.layerStatesArray.sort(function(t,e){return t.zIndex-e.zIndex}),n=e.viewState;this.children_.length=0;for(var i=null,o=0,a=r.length;o=0;--s){var u=a[s],l=u.layer;if(l.hasRenderer()&&Ui(u,o)&&i(l)){var h=l.getRenderer().getDataAtPixel(t,e,r);if(h){var c=n(l,h);if(c)return c}}}},e}(Za),qa=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Ja=function(t){function e(e){return(e=p({},e)).controls||(e.controls=qi()),e.interactions||(e.interactions=Jo()),t.call(this,e)||this}return qa(e,t),e.prototype.createRenderer=function(){return new Ha(this)},e}(Ii),Qa={BOTTOM_LEFT:"bottom-left",BOTTOM_CENTER:"bottom-center",BOTTOM_RIGHT:"bottom-right",CENTER_LEFT:"center-left",CENTER_CENTER:"center-center",CENTER_RIGHT:"center-right",TOP_LEFT:"top-left",TOP_CENTER:"top-center",TOP_RIGHT:"top-right"},$a=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),ts={ELEMENT:"element",MAP:"map",OFFSET:"offset",POSITION:"position",POSITIONING:"positioning"},es=function(t){function e(e){var r=t.call(this)||this;return r.options=e,r.id=e.id,r.insertFirst=void 0===e.insertFirst||e.insertFirst,r.stopEvent=void 0===e.stopEvent||e.stopEvent,r.element=document.createElement("div"),r.element.className=void 0!==e.className?e.className:"ol-overlay-container "+Fi,r.element.style.position="absolute",r.autoPan=void 0!==e.autoPan&&e.autoPan,r.autoPanAnimation=e.autoPanAnimation||{},r.autoPanMargin=void 0!==e.autoPanMargin?e.autoPanMargin:20,r.rendered={bottom_:"",left_:"",right_:"",top_:"",visible:!0},r.mapPostrenderListenerKey=null,r.addEventListener(Y(ts.ELEMENT),r.handleElementChanged),r.addEventListener(Y(ts.MAP),r.handleMapChanged),r.addEventListener(Y(ts.OFFSET),r.handleOffsetChanged),r.addEventListener(Y(ts.POSITION),r.handlePositionChanged),r.addEventListener(Y(ts.POSITIONING),r.handlePositioningChanged),void 0!==e.element&&r.setElement(e.element),r.setOffset(void 0!==e.offset?e.offset:[0,0]),r.setPositioning(void 0!==e.positioning?e.positioning:Qa.TOP_LEFT),void 0!==e.position&&r.setPosition(e.position),r}return $a(e,t),e.prototype.getElement=function(){return this.get(ts.ELEMENT)},e.prototype.getId=function(){return this.id},e.prototype.getMap=function(){return this.get(ts.MAP)},e.prototype.getOffset=function(){return this.get(ts.OFFSET)},e.prototype.getPosition=function(){return this.get(ts.POSITION)},e.prototype.getPositioning=function(){return this.get(ts.POSITIONING)},e.prototype.handleElementChanged=function(){di(this.element);var t=this.getElement();t&&this.element.appendChild(t)},e.prototype.handleMapChanged=function(){this.mapPostrenderListenerKey&&(fi(this.element),v(this.mapPostrenderListenerKey),this.mapPostrenderListenerKey=null);var t=this.getMap();if(t){this.mapPostrenderListenerKey=g(t,dn,this.render,this),this.updatePixelPosition();var e=this.stopEvent?t.getOverlayContainerStopEvent():t.getOverlayContainer();this.insertFirst?e.insertBefore(this.element,e.childNodes[0]||null):e.appendChild(this.element)}},e.prototype.render=function(){this.updatePixelPosition()},e.prototype.handleOffsetChanged=function(){this.updatePixelPosition()},e.prototype.handlePositionChanged=function(){this.updatePixelPosition(),this.get(ts.POSITION)&&this.autoPan&&this.panIntoView()},e.prototype.handlePositioningChanged=function(){this.updatePixelPosition()},e.prototype.setElement=function(t){this.set(ts.ELEMENT,t)},e.prototype.setMap=function(t){this.set(ts.MAP,t)},e.prototype.setOffset=function(t){this.set(ts.OFFSET,t)},e.prototype.setPosition=function(t){this.set(ts.POSITION,t)},e.prototype.panIntoView=function(){var t=this.getMap();if(t&&t.getTargetElement()){var e=this.getRect(t.getTargetElement(),t.getSize()),r=this.getElement(),n=this.getRect(r,[hi(r),ci(r)]),i=this.autoPanMargin;if(!it(e,n)){var o=n[0]-e[0],a=e[2]-n[2],s=n[1]-e[1],u=e[3]-n[3],l=[0,0];if(o<0?l[0]=o-i:a<0&&(l[0]=Math.abs(a)+i),s<0?l[1]=s-i:u<0&&(l[1]=Math.abs(u)+i),0!==l[0]||0!==l[1]){var h=t.getView().getCenterInternal(),c=t.getPixelFromCoordinateInternal(h),p=[c[0]+l[0],c[1]+l[1]];t.getView().animateInternal({center:t.getCoordinateFromPixelInternal(p),duration:this.autoPanAnimation.duration,easing:this.autoPanAnimation.easing})}}}},e.prototype.getRect=function(t,e){var r=t.getBoundingClientRect(),n=r.left+window.pageXOffset,i=r.top+window.pageYOffset;return[n,i,n+e[0],i+e[1]]},e.prototype.setPositioning=function(t){this.set(ts.POSITIONING,t)},e.prototype.setVisible=function(t){this.rendered.visible!==t&&(this.element.style.display=t?"":"none",this.rendered.visible=t)},e.prototype.updatePixelPosition=function(){var t=this.getMap(),e=this.getPosition();if(t&&t.isRendered()&&e){var r=t.getPixelFromCoordinate(e),n=t.getSize();this.updateRenderedPosition(r,n)}else this.setVisible(!1)},e.prototype.updateRenderedPosition=function(t,e){var r=this.element.style,n=this.getOffset(),i=this.getPositioning();this.setVisible(!0);var o=n[0],a=n[1];if(i==Qa.BOTTOM_RIGHT||i==Qa.CENTER_RIGHT||i==Qa.TOP_RIGHT){""!==this.rendered.left_&&(this.rendered.left_="",r.left="");var s=Math.round(e[0]-t[0]-o)+"px";this.rendered.right_!=s&&(this.rendered.right_=s,r.right=s)}else{""!==this.rendered.right_&&(this.rendered.right_="",r.right=""),i!=Qa.BOTTOM_CENTER&&i!=Qa.CENTER_CENTER&&i!=Qa.TOP_CENTER||(o-=this.element.offsetWidth/2);var u=Math.round(t[0]+o)+"px";this.rendered.left_!=u&&(this.rendered.left_=u,r.left=u)}if(i==Qa.BOTTOM_LEFT||i==Qa.BOTTOM_CENTER||i==Qa.BOTTOM_RIGHT){""!==this.rendered.top_&&(this.rendered.top_="",r.top="");var l=Math.round(e[1]-t[1]-a)+"px";this.rendered.bottom_!=l&&(this.rendered.bottom_=l,r.bottom=l)}else{""!==this.rendered.bottom_&&(this.rendered.bottom_="",r.bottom=""),i!=Qa.CENTER_LEFT&&i!=Qa.CENTER_CENTER&&i!=Qa.CENTER_RIGHT||(a-=this.element.offsetHeight/2);var h=Math.round(t[1]+a)+"px";this.rendered.top_!=h&&(this.rendered.top_="top",r.top=h)}},e.prototype.getOptions=function(){return this.options},e}(z),rs={ARRAY_BUFFER:"arraybuffer",JSON:"json",TEXT:"text",XML:"xml"},ns=!1;function is(t,e,r,n){return function(i,o,a){var s=new XMLHttpRequest;s.open("GET","function"==typeof t?t(i,o,a):t,!0),e.getType()==rs.ARRAY_BUFFER&&(s.responseType="arraybuffer"),s.withCredentials=ns,s.onload=function(t){if(!s.status||s.status>=200&&s.status<300){var o=e.getType(),u=void 0;o==rs.JSON||o==rs.TEXT?u=s.responseText:o==rs.XML?(u=s.responseXML)||(u=(new DOMParser).parseFromString(s.responseText,"application/xml")):o==rs.ARRAY_BUFFER&&(u=s.response),u?r.call(this,e.readFeatures(u,{extent:i,featureProjection:a}),e.readProjection(u)):n.call(this)}else n.call(this)}.bind(this),s.onerror=function(){n.call(this)}.bind(this),s.send()}}function os(t,e){return is(t,e,function(t,e){"function"==typeof this.addFeatures&&this.addFeatures(t)},b)}function as(t,e){return[[-1/0,-1/0,1/0,1/0]]}function ss(t,e){return[t]}var us=function(){function t(t,e,r,n){this.minX=t,this.maxX=e,this.minY=r,this.maxY=n}return t.prototype.contains=function(t){return this.containsXY(t[1],t[2])},t.prototype.containsTileRange=function(t){return this.minX<=t.minX&&t.maxX<=this.maxX&&this.minY<=t.minY&&t.maxY<=this.maxY},t.prototype.containsXY=function(t,e){return this.minX<=t&&t<=this.maxX&&this.minY<=e&&e<=this.maxY},t.prototype.equals=function(t){return this.minX==t.minX&&this.minY==t.minY&&this.maxX==t.maxX&&this.maxY==t.maxY},t.prototype.extend=function(t){t.minXthis.maxX&&(this.maxX=t.maxX),t.minYthis.maxY&&(this.maxY=t.maxY)},t.prototype.getHeight=function(){return this.maxY-this.minY+1},t.prototype.getSize=function(){return[this.getWidth(),this.getHeight()]},t.prototype.getWidth=function(){return this.maxX-this.minX+1},t.prototype.intersects=function(t){return this.minX<=t.maxX&&this.maxX>=t.minX&&this.minY<=t.maxY&&this.maxY>=t.minY},t}();function ls(t,e,r,n,i){return void 0!==i?(i.minX=t,i.maxX=e,i.minY=r,i.maxY=n,i):new us(t,e,r,n)}var hs=us;function cs(t,e,r,n){return void 0!==n?(n[0]=t,n[1]=e,n[2]=r,n):[t,e,r]}function ps(t,e,r){return t+"/"+e+"/"+r}function fs(t){return ps(t[0],t[1],t[2])}function ds(t){return(t[1]<0||r&&0===o)})),17),!t.origins)for(var o=0,a=this.resolutions_.length-1;o=this.minZoom;){if(e(s,2===this.zoomFactor_?ls(i=Math.floor(i/2),i,o=Math.floor(o/2),o,r):this.getTileRangeForExtentAndZ(a,s,r)))return!0;--s}return!1},t.prototype.getExtent=function(){return this.extent_},t.prototype.getMaxZoom=function(){return this.maxZoom},t.prototype.getMinZoom=function(){return this.minZoom},t.prototype.getOrigin=function(t){return this.origin_?this.origin_:this.origins_[t]},t.prototype.getResolution=function(t){return this.resolutions_[t]},t.prototype.getResolutions=function(){return this.resolutions_},t.prototype.getTileCoordChildTileRange=function(t,e,r){if(t[0]=0;o--)this.postProcessPasses_[o].init(t);e.bindTexture(e.TEXTURE_2D,null),e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT),e.enable(e.BLEND),e.blendFunc(e.ONE,e.ONE_MINUS_SRC_ALPHA),e.useProgram(this.currentProgram_),this.applyFrameState(t),this.applyUniforms(t)},e.prototype.prepareDrawToRenderTarget=function(t,e,r){var n=this.getGL(),i=e.getSize();n.bindFramebuffer(n.FRAMEBUFFER,e.getFramebuffer()),n.viewport(0,0,i[0],i[1]),n.bindTexture(n.TEXTURE_2D,e.getTexture()),n.clearColor(0,0,0,0),n.clear(n.COLOR_BUFFER_BIT),n.enable(n.BLEND),n.blendFunc(n.ONE,r?n.ZERO:n.ONE_MINUS_SRC_ALPHA),n.useProgram(this.currentProgram_),this.applyFrameState(t),this.applyUniforms(t)},e.prototype.drawElements=function(t,e){var r=this.getGL(),n=r.UNSIGNED_INT,i=e-t,o=4*t;r.drawElements(r.TRIANGLES,i,n,o)},e.prototype.finalizeDraw=function(t){for(var e=0;ethis.size_[0]||e>=this.size_[1])return lu[0]=0,lu[1]=0,lu[2]=0,lu[3]=0,lu;this.readAll();var r=Math.floor(t)+(this.size_[1]-Math.floor(e)-1)*this.size_[0];return lu[0]=this.data_[4*r],lu[1]=this.data_[4*r+1],lu[2]=this.data_[4*r+2],lu[3]=this.data_[4*r+3],lu},t.prototype.getTexture=function(){return this.texture_},t.prototype.getFramebuffer=function(){return this.framebuffer_},t.prototype.updateSize_=function(){var t=this.size_,e=this.helper_.getGL();this.texture_=this.helper_.createTexture(t,null,this.texture_),e.bindFramebuffer(e.FRAMEBUFFER,this.framebuffer_),e.viewport(0,0,t[0],t[1]),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,this.texture_,0),this.data_=new Uint8Array(t[0]*t[1]*4)},t}(),cu=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),pu=function(t){function e(e){var r=t.call(this,{extent:e.extent,origin:e.origin,origins:e.origins,resolutions:e.resolutions,tileSize:e.tileSize,tileSizes:e.tileSizes,sizes:e.sizes})||this;return r.matrixIds_=e.matrixIds,r}return cu(e,t),e.prototype.getMatrixId=function(t){return this.matrixIds_[t]},e.prototype.getMatrixIds=function(){return this.matrixIds_},e}(gs),fu=pu;function du(t,e,r){var n=[],i=[],o=[],a=[],s=[],u=void 0!==r?r:[],l=t.SupportedCRS,h=we(l.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"))||we(l),c=h.getMetersPerUnit(),p="ne"==h.getAxisOrientation().substr(0,2);return t.TileMatrix.sort(function(t,e){return e.ScaleDenominator-t.ScaleDenominator}),t.TileMatrix.forEach(function(e){if(!(u.length>0)||O(u,function(r){return e.Identifier==r.TileMatrix||-1===e.Identifier.indexOf(":")&&t.Identifier+":"+e.Identifier===r.TileMatrix})){i.push(e.Identifier);var r=28e-5*e.ScaleDenominator/c,l=e.TileWidth,h=e.TileHeight;p?o.push([e.TopLeftCorner[1],e.TopLeftCorner[0]]):o.push(e.TopLeftCorner),n.push(r),a.push(l==h?l:[l,h]),s.push([e.MatrixWidth,e.MatrixHeight])}}),new pu({extent:e,origins:o,resolutions:n,matrixIds:i,tileSizes:a,sizes:s})}var _u=function(){function t(t){this.opacity_=t.opacity,this.rotateWithView_=t.rotateWithView,this.rotation_=t.rotation,this.scale_=t.scale}return t.prototype.clone=function(){return new t({opacity:this.getOpacity(),scale:this.getScale(),rotation:this.getRotation(),rotateWithView:this.getRotateWithView()})},t.prototype.getOpacity=function(){return this.opacity_},t.prototype.getRotateWithView=function(){return this.rotateWithView_},t.prototype.getRotation=function(){return this.rotation_},t.prototype.getScale=function(){return this.scale_},t.prototype.getAnchor=function(){return n()},t.prototype.getImage=function(t){return n()},t.prototype.getHitDetectionImage=function(t){return n()},t.prototype.getImageState=function(){return n()},t.prototype.getImageSize=function(){return n()},t.prototype.getHitDetectionImageSize=function(){return n()},t.prototype.getOrigin=function(){return n()},t.prototype.getSize=function(){return n()},t.prototype.setOpacity=function(t){this.opacity_=t},t.prototype.setRotateWithView=function(t){this.rotateWithView_=t},t.prototype.setRotation=function(t){this.rotation_=t},t.prototype.setScale=function(t){this.scale_=t},t.prototype.listenImageChange=function(t){n()},t.prototype.load=function(){n()},t.prototype.unlistenImageChange=function(t){n()},t}(),gu=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),yu=function(t){function e(e){var r=this,n=void 0!==e.rotateWithView&&e.rotateWithView;return(r=t.call(this,{opacity:1,rotateWithView:n,rotation:void 0!==e.rotation?e.rotation:0,scale:1})||this).canvas_=null,r.hitDetectionCanvas_=null,r.fill_=void 0!==e.fill?e.fill:null,r.origin_=[0,0],r.points_=e.points,r.radius_=void 0!==e.radius?e.radius:e.radius1,r.radius2_=e.radius2,r.angle_=void 0!==e.angle?e.angle:0,r.stroke_=void 0!==e.stroke?e.stroke:null,r.anchor_=null,r.size_=null,r.imageSize_=null,r.hitDetectionImageSize_=null,r.render(),r}return gu(e,t),e.prototype.clone=function(){var t=new e({fill:this.getFill()?this.getFill().clone():void 0,points:this.getPoints(),radius:this.getRadius(),radius2:this.getRadius2(),angle:this.getAngle(),stroke:this.getStroke()?this.getStroke().clone():void 0,rotation:this.getRotation(),rotateWithView:this.getRotateWithView()});return t.setOpacity(this.getOpacity()),t.setScale(this.getScale()),t},e.prototype.getAnchor=function(){return this.anchor_},e.prototype.getAngle=function(){return this.angle_},e.prototype.getFill=function(){return this.fill_},e.prototype.getHitDetectionImage=function(t){return this.hitDetectionCanvas_},e.prototype.getImage=function(t){return this.canvas_},e.prototype.getImageSize=function(){return this.imageSize_},e.prototype.getHitDetectionImageSize=function(){return this.hitDetectionImageSize_},e.prototype.getImageState=function(){return Da.LOADED},e.prototype.getOrigin=function(){return this.origin_},e.prototype.getPoints=function(){return this.points_},e.prototype.getRadius=function(){return this.radius_},e.prototype.getRadius2=function(){return this.radius2_},e.prototype.getSize=function(){return this.size_},e.prototype.getStroke=function(){return this.stroke_},e.prototype.listenImageChange=function(t){},e.prototype.load=function(){},e.prototype.unlistenImageChange=function(t){},e.prototype.render=function(){var t,e="round",r="round",n=0,i=null,o=0,a=0;this.stroke_&&(null===(t=this.stroke_.getColor())&&(t="#000"),t=ca(t),void 0===(a=this.stroke_.getWidth())&&(a=1),i=this.stroke_.getLineDash(),o=this.stroke_.getLineDashOffset(),void 0===(r=this.stroke_.getLineJoin())&&(r="round"),void 0===(e=this.stroke_.getLineCap())&&(e="round"),void 0===(n=this.stroke_.getMiterLimit())&&(n=10));var s=2*(this.radius_+a)+1,u={strokeStyle:t,strokeWidth:a,size:s,lineCap:e,lineDash:i,lineDashOffset:o,lineJoin:r,miterLimit:n},l=li(s,s);this.canvas_=l.canvas;var h=s=this.canvas_.width;this.draw_(u,l,0,0),this.createHitDetectionCanvas_(u),this.anchor_=[s/2,s/2],this.size_=[s,s],this.imageSize_=[h,h]},e.prototype.draw_=function(t,e,r,n){var i,o,a;e.setTransform(1,0,0,1,0,0),e.translate(r,n),e.beginPath();var s=this.points_;if(s===1/0)e.arc(t.size/2,t.size/2,this.radius_,0,2*Math.PI,!0);else{var u=void 0!==this.radius2_?this.radius2_:this.radius_;for(u!==this.radius_&&(s*=2),i=0;i<=s;i++)o=2*i*Math.PI/s-Math.PI/2+this.angle_,a=i%2==0?this.radius_:u,e.lineTo(t.size/2+a*Math.cos(o),t.size/2+a*Math.sin(o))}if(this.fill_){var l=this.fill_.getColor();null===l&&(l="#000"),e.fillStyle=ca(l),e.fill()}this.stroke_&&(e.strokeStyle=t.strokeStyle,e.lineWidth=t.strokeWidth,e.setLineDash&&t.lineDash&&(e.setLineDash(t.lineDash),e.lineDashOffset=t.lineDashOffset),e.lineCap=t.lineCap,e.lineJoin=t.lineJoin,e.miterLimit=t.miterLimit,e.stroke()),e.closePath()},e.prototype.createHitDetectionCanvas_=function(t){if(this.hitDetectionImageSize_=[t.size,t.size],this.hitDetectionCanvas_=this.canvas_,this.fill_){var e=this.fill_.getColor(),r=0;if("string"==typeof e&&(e=oa(e)),null===e?r=1:Array.isArray(e)&&(r=4===e.length?e[3]:1),0===r){var n=li(t.size,t.size);this.hitDetectionCanvas_=n.canvas,this.drawHitDetectionCanvas_(t,n,0,0)}}},e.prototype.drawHitDetectionCanvas_=function(t,e,r,n){e.setTransform(1,0,0,1,0,0),e.translate(r,n),e.beginPath();var i=this.points_;if(i===1/0)e.arc(t.size/2,t.size/2,this.radius_,0,2*Math.PI,!0);else{var o=void 0!==this.radius2_?this.radius2_:this.radius_;o!==this.radius_&&(i*=2);var a=void 0,s=void 0,u=void 0;for(a=0;a<=i;a++)u=2*a*Math.PI/i-Math.PI/2+this.angle_,s=a%2==0?this.radius_:o,e.lineTo(t.size/2+s*Math.cos(u),t.size/2+s*Math.sin(u))}e.fillStyle="#000",e.fill(),this.stroke_&&(e.strokeStyle=t.strokeStyle,e.lineWidth=t.strokeWidth,t.lineDash&&(e.setLineDash(t.lineDash),e.lineDashOffset=t.lineDashOffset),e.stroke()),e.closePath()},e}(_u),vu=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),mu=function(t){function e(e){var r=e||{};return t.call(this,{points:1/0,fill:r.fill,radius:r.radius,stroke:r.stroke})||this}return vu(e,t),e.prototype.clone=function(){var t=new e({fill:this.getFill()?this.getFill().clone():void 0,stroke:this.getStroke()?this.getStroke().clone():void 0,radius:this.getRadius()});return t.setOpacity(this.getOpacity()),t.setScale(this.getScale()),t},e.prototype.setRadius=function(t){this.radius_=t,this.render()},e}(yu),Eu=function(){function t(t){var e=t||{};this.color_=void 0!==e.color?e.color:null}return t.prototype.clone=function(){var e=this.getColor();return new t({color:Array.isArray(e)?e.slice():e||void 0})},t.prototype.getColor=function(){return this.color_},t.prototype.setColor=function(t){this.color_=t},t}(),Tu={FRACTION:"fraction",PIXELS:"pixels"},Su=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),wu=function(t){function e(e,r,n,i){var o=t.call(this)||this;return o.extent=e,o.pixelRatio_=n,o.resolution=r,o.state=i,o}return Su(e,t),e.prototype.changed=function(){this.dispatchEvent(N.CHANGE)},e.prototype.getExtent=function(){return this.extent},e.prototype.getImage=function(){return n()},e.prototype.getPixelRatio=function(){return this.pixelRatio_},e.prototype.getResolution=function(){return this.resolution},e.prototype.getState=function(){return this.state},e.prototype.load=function(){n()},e}(A),xu=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function Ou(t,e,r){var n=t;if(n.src&&sn){var i=n.decode(),o=!0;return i.then(function(){o&&e()}).catch(function(t){o&&("EncodingError"===t.name&&"Invalid image type."===t.message?e():r())}),function(){o=!1}}var a=[y(n,N.LOAD,e),y(n,N.ERROR,r)];return function(){a.forEach(v)}}var Ru=function(t){function e(e,r,n,i,o,a){var s=t.call(this,e,r,n,Da.IDLE)||this;return s.src_=i,s.image_=new Image,null!==o&&(s.image_.crossOrigin=o),s.unlisten_=null,s.state=Da.IDLE,s.imageLoadFunction_=a,s}return xu(e,t),e.prototype.getImage=function(){return this.image_},e.prototype.handleImageError_=function(){this.state=Da.ERROR,this.unlistenImage_(),this.changed()},e.prototype.handleImageLoad_=function(){void 0===this.resolution&&(this.resolution=Ot(this.extent)/this.image_.height),this.state=Da.LOADED,this.unlistenImage_(),this.changed()},e.prototype.load=function(){this.state!=Da.IDLE&&this.state!=Da.ERROR||(this.state=Da.LOADING,this.changed(),this.imageLoadFunction_(this,this.src_),this.unlisten_=Ou(this.image_,this.handleImageLoad_.bind(this),this.handleImageError_.bind(this)))},e.prototype.setImage=function(t){this.image_=t},e.prototype.unlistenImage_=function(){this.unlisten_&&(this.unlisten_(),this.unlisten_=null)},e}(wu),Cu=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Pu=function(t){function e(e,r,n,i,o,a){var s=t.call(this)||this;return s.hitDetectionImage_=null,s.image_=e||new Image,null!==i&&(s.image_.crossOrigin=i),s.canvas_=a?document.createElement("canvas"):null,s.color_=a,s.unlisten_=null,s.imageState_=o,s.size_=n,s.src_=r,s.tainted_,s}return Cu(e,t),e.prototype.isTainted_=function(){if(void 0===this.tainted_&&this.imageState_===Da.LOADED){this.tainted_=!1;var t=li(1,1);try{t.drawImage(this.image_,0,0),t.getImageData(0,0,1,1)}catch(t){this.tainted_=!0}}return!0===this.tainted_},e.prototype.dispatchChangeEvent_=function(){this.dispatchEvent(N.CHANGE)},e.prototype.handleImageError_=function(){this.imageState_=Da.ERROR,this.unlistenImage_(),this.dispatchChangeEvent_()},e.prototype.handleImageLoad_=function(){this.imageState_=Da.LOADED,this.size_&&(this.image_.width=this.size_[0],this.image_.height=this.size_[1]),this.size_=[this.image_.width,this.image_.height],this.unlistenImage_(),this.replaceColor_(),this.dispatchChangeEvent_()},e.prototype.getImage=function(t){return this.canvas_?this.canvas_:this.image_},e.prototype.getImageState=function(){return this.imageState_},e.prototype.getHitDetectionImage=function(t){if(!this.hitDetectionImage_)if(this.isTainted_()){var e=this.size_[0],r=this.size_[1],n=li(e,r);n.fillRect(0,0,e,r),this.hitDetectionImage_=n.canvas}else this.hitDetectionImage_=this.image_;return this.hitDetectionImage_},e.prototype.getSize=function(){return this.size_},e.prototype.getSrc=function(){return this.src_},e.prototype.load=function(){if(this.imageState_==Da.IDLE){this.imageState_=Da.LOADING;try{this.image_.src=this.src_}catch(t){this.handleImageError_()}this.unlisten_=Ou(this.image_,this.handleImageLoad_.bind(this),this.handleImageError_.bind(this))}},e.prototype.replaceColor_=function(){if(this.color_&&!this.isTainted_()){this.canvas_.width=this.image_.width,this.canvas_.height=this.image_.height;var t=this.canvas_.getContext("2d");t.drawImage(this.image_,0,0);for(var e=t.getImageData(0,0,this.image_.width,this.image_.height),r=e.data,n=this.color_[0]/255,i=this.color_[1]/255,o=this.color_[2]/255,a=0,s=r.length;a0,6);var p=void 0!==n.src?Da.IDLE:Da.LOADED;return r.color_=void 0!==n.color?oa(n.color):null,r.iconImage_=function(t,e,r,n,i,o){var a=ha.get(e,n,o);return a||(a=new Pu(t,e,r,n,i,o),ha.set(e,n,o,a)),a}(l,c,h,r.crossOrigin_,p,r.color_),r.offset_=void 0!==n.offset?n.offset:[0,0],r.offsetOrigin_=void 0!==n.offsetOrigin?n.offsetOrigin:Iu.TOP_LEFT,r.origin_=null,r.size_=void 0!==n.size?n.size:null,r}return bu(e,t),e.prototype.clone=function(){return new e({anchor:this.anchor_.slice(),anchorOrigin:this.anchorOrigin_,anchorXUnits:this.anchorXUnits_,anchorYUnits:this.anchorYUnits_,crossOrigin:this.crossOrigin_,color:this.color_&&this.color_.slice?this.color_.slice():this.color_||void 0,src:this.getSrc(),offset:this.offset_.slice(),offsetOrigin:this.offsetOrigin_,size:null!==this.size_?this.size_.slice():void 0,opacity:this.getOpacity(),scale:this.getScale(),rotation:this.getRotation(),rotateWithView:this.getRotateWithView()})},e.prototype.getAnchor=function(){if(this.normalizedAnchor_)return this.normalizedAnchor_;var t=this.anchor_,e=this.getSize();if(this.anchorXUnits_==Tu.FRACTION||this.anchorYUnits_==Tu.FRACTION){if(!e)return null;t=this.anchor_.slice(),this.anchorXUnits_==Tu.FRACTION&&(t[0]*=e[0]),this.anchorYUnits_==Tu.FRACTION&&(t[1]*=e[1])}if(this.anchorOrigin_!=Iu.TOP_LEFT){if(!e)return null;t===this.anchor_&&(t=this.anchor_.slice()),this.anchorOrigin_!=Iu.TOP_RIGHT&&this.anchorOrigin_!=Iu.BOTTOM_RIGHT||(t[0]=-t[0]+e[0]),this.anchorOrigin_!=Iu.BOTTOM_LEFT&&this.anchorOrigin_!=Iu.BOTTOM_RIGHT||(t[1]=-t[1]+e[1])}return this.normalizedAnchor_=t,this.normalizedAnchor_},e.prototype.setAnchor=function(t){this.anchor_=t,this.normalizedAnchor_=null},e.prototype.getColor=function(){return this.color_},e.prototype.getImage=function(t){return this.iconImage_.getImage(t)},e.prototype.getImageSize=function(){return this.iconImage_.getSize()},e.prototype.getHitDetectionImageSize=function(){return this.getImageSize()},e.prototype.getImageState=function(){return this.iconImage_.getImageState()},e.prototype.getHitDetectionImage=function(t){return this.iconImage_.getHitDetectionImage(t)},e.prototype.getOrigin=function(){if(this.origin_)return this.origin_;var t=this.offset_;if(this.offsetOrigin_!=Iu.TOP_LEFT){var e=this.getSize(),r=this.iconImage_.getSize();if(!e||!r)return null;t=t.slice(),this.offsetOrigin_!=Iu.TOP_RIGHT&&this.offsetOrigin_!=Iu.BOTTOM_RIGHT||(t[0]=r[0]-e[0]-t[0]),this.offsetOrigin_!=Iu.BOTTOM_LEFT&&this.offsetOrigin_!=Iu.BOTTOM_RIGHT||(t[1]=r[1]-e[1]-t[1])}return this.origin_=t,this.origin_},e.prototype.getSrc=function(){return this.iconImage_.getSrc()},e.prototype.getSize=function(){return this.size_?this.size_:this.iconImage_.getSize()},e.prototype.listenImageChange=function(t){this.iconImage_.addEventListener(N.CHANGE,t)},e.prototype.load=function(){this.iconImage_.load()},e.prototype.unlistenImageChange=function(t){this.iconImage_.removeEventListener(N.CHANGE,t)},e}(_u),Mu=function(){function t(t){var e=t||{};this.color_=void 0!==e.color?e.color:null,this.lineCap_=e.lineCap,this.lineDash_=void 0!==e.lineDash?e.lineDash:null,this.lineDashOffset_=e.lineDashOffset,this.lineJoin_=e.lineJoin,this.miterLimit_=e.miterLimit,this.width_=e.width}return t.prototype.clone=function(){var e=this.getColor();return new t({color:Array.isArray(e)?e.slice():e||void 0,lineCap:this.getLineCap(),lineDash:this.getLineDash()?this.getLineDash().slice():void 0,lineDashOffset:this.getLineDashOffset(),lineJoin:this.getLineJoin(),miterLimit:this.getMiterLimit(),width:this.getWidth()})},t.prototype.getColor=function(){return this.color_},t.prototype.getLineCap=function(){return this.lineCap_},t.prototype.getLineDash=function(){return this.lineDash_},t.prototype.getLineDashOffset=function(){return this.lineDashOffset_},t.prototype.getLineJoin=function(){return this.lineJoin_},t.prototype.getMiterLimit=function(){return this.miterLimit_},t.prototype.getWidth=function(){return this.width_},t.prototype.setColor=function(t){this.color_=t},t.prototype.setLineCap=function(t){this.lineCap_=t},t.prototype.setLineDash=function(t){this.lineDash_=t},t.prototype.setLineDashOffset=function(t){this.lineDashOffset_=t},t.prototype.setLineJoin=function(t){this.lineJoin_=t},t.prototype.setMiterLimit=function(t){this.miterLimit_=t},t.prototype.setWidth=function(t){this.width_=t},t}(),Fu=function(){function t(t){var e=t||{};this.geometry_=null,this.geometryFunction_=Gu,void 0!==e.geometry&&this.setGeometry(e.geometry),this.fill_=void 0!==e.fill?e.fill:null,this.image_=void 0!==e.image?e.image:null,this.renderer_=void 0!==e.renderer?e.renderer:null,this.stroke_=void 0!==e.stroke?e.stroke:null,this.text_=void 0!==e.text?e.text:null,this.zIndex_=e.zIndex}return t.prototype.clone=function(){var e=this.getGeometry();return e&&"object"==typeof e&&(e=e.clone()),new t({geometry:e,fill:this.getFill()?this.getFill().clone():void 0,image:this.getImage()?this.getImage().clone():void 0,stroke:this.getStroke()?this.getStroke().clone():void 0,text:this.getText()?this.getText().clone():void 0,zIndex:this.getZIndex()})},t.prototype.getRenderer=function(){return this.renderer_},t.prototype.setRenderer=function(t){this.renderer_=t},t.prototype.getGeometry=function(){return this.geometry_},t.prototype.getGeometryFunction=function(){return this.geometryFunction_},t.prototype.getFill=function(){return this.fill_},t.prototype.setFill=function(t){this.fill_=t},t.prototype.getImage=function(){return this.image_},t.prototype.setImage=function(t){this.image_=t},t.prototype.getStroke=function(){return this.stroke_},t.prototype.setStroke=function(t){this.stroke_=t},t.prototype.getText=function(){return this.text_},t.prototype.setText=function(t){this.text_=t},t.prototype.getZIndex=function(){return this.zIndex_},t.prototype.setGeometry=function(t){"function"==typeof t?this.geometryFunction_=t:"string"==typeof t?this.geometryFunction_=function(e){return e.get(t)}:t?void 0!==t&&(this.geometryFunction_=function(){return t}):this.geometryFunction_=Gu,this.geometry_=t},t.prototype.setZIndex=function(t){this.zIndex_=t},t}();var Au=null;function Nu(t,e){if(!Au){var r=new Eu({color:"rgba(255,255,255,0.4)"}),n=new Mu({color:"#3399CC",width:1.25});Au=[new Fu({image:new mu({fill:r,stroke:n,radius:5}),fill:r,stroke:n})]}return Au}function Du(){var t={},e=[255,255,255,1],r=[0,153,255,1];return t[Nt.POLYGON]=[new Fu({fill:new Eu({color:[255,255,255,.5]})})],t[Nt.MULTI_POLYGON]=t[Nt.POLYGON],t[Nt.LINE_STRING]=[new Fu({stroke:new Mu({color:e,width:5})}),new Fu({stroke:new Mu({color:r,width:3})})],t[Nt.MULTI_LINE_STRING]=t[Nt.LINE_STRING],t[Nt.CIRCLE]=t[Nt.POLYGON].concat(t[Nt.LINE_STRING]),t[Nt.POINT]=[new Fu({image:new mu({radius:6,fill:new Eu({color:r}),stroke:new Mu({color:e,width:1.5})}),zIndex:1/0})],t[Nt.MULTI_POINT]=t[Nt.POINT],t[Nt.GEOMETRY_COLLECTION]=t[Nt.POLYGON].concat(t[Nt.LINE_STRING],t[Nt.POINT]),t}function Gu(t){return t.getGeometry()}var ju=Fu,ku={POINT:"point",LINE:"line"},Uu="#333",Bu=function(){function t(t){var e=t||{};this.font_=e.font,this.rotation_=e.rotation,this.rotateWithView_=e.rotateWithView,this.scale_=e.scale,this.text_=e.text,this.textAlign_=e.textAlign,this.textBaseline_=e.textBaseline,this.fill_=void 0!==e.fill?e.fill:new Eu({color:Uu}),this.maxAngle_=void 0!==e.maxAngle?e.maxAngle:Math.PI/4,this.placement_=void 0!==e.placement?e.placement:ku.POINT,this.overflow_=!!e.overflow,this.stroke_=void 0!==e.stroke?e.stroke:null,this.offsetX_=void 0!==e.offsetX?e.offsetX:0,this.offsetY_=void 0!==e.offsetY?e.offsetY:0,this.backgroundFill_=e.backgroundFill?e.backgroundFill:null,this.backgroundStroke_=e.backgroundStroke?e.backgroundStroke:null,this.padding_=void 0===e.padding?null:e.padding}return t.prototype.clone=function(){return new t({font:this.getFont(),placement:this.getPlacement(),maxAngle:this.getMaxAngle(),overflow:this.getOverflow(),rotation:this.getRotation(),rotateWithView:this.getRotateWithView(),scale:this.getScale(),text:this.getText(),textAlign:this.getTextAlign(),textBaseline:this.getTextBaseline(),fill:this.getFill()?this.getFill().clone():void 0,stroke:this.getStroke()?this.getStroke().clone():void 0,offsetX:this.getOffsetX(),offsetY:this.getOffsetY(),backgroundFill:this.getBackgroundFill()?this.getBackgroundFill().clone():void 0,backgroundStroke:this.getBackgroundStroke()?this.getBackgroundStroke().clone():void 0,padding:this.getPadding()})},t.prototype.getOverflow=function(){return this.overflow_},t.prototype.getFont=function(){return this.font_},t.prototype.getMaxAngle=function(){return this.maxAngle_},t.prototype.getPlacement=function(){return this.placement_},t.prototype.getOffsetX=function(){return this.offsetX_},t.prototype.getOffsetY=function(){return this.offsetY_},t.prototype.getFill=function(){return this.fill_},t.prototype.getRotateWithView=function(){return this.rotateWithView_},t.prototype.getRotation=function(){return this.rotation_},t.prototype.getScale=function(){return this.scale_},t.prototype.getStroke=function(){return this.stroke_},t.prototype.getText=function(){return this.text_},t.prototype.getTextAlign=function(){return this.textAlign_},t.prototype.getTextBaseline=function(){return this.textBaseline_},t.prototype.getBackgroundFill=function(){return this.backgroundFill_},t.prototype.getBackgroundStroke=function(){return this.backgroundStroke_},t.prototype.getPadding=function(){return this.padding_},t.prototype.setOverflow=function(t){this.overflow_=t},t.prototype.setFont=function(t){this.font_=t},t.prototype.setMaxAngle=function(t){this.maxAngle_=t},t.prototype.setOffsetX=function(t){this.offsetX_=t},t.prototype.setOffsetY=function(t){this.offsetY_=t},t.prototype.setPlacement=function(t){this.placement_=t},t.prototype.setRotateWithView=function(t){this.rotateWithView_=t},t.prototype.setFill=function(t){this.fill_=t},t.prototype.setRotation=function(t){this.rotation_=t},t.prototype.setScale=function(t){this.scale_=t},t.prototype.setStroke=function(t){this.stroke_=t},t.prototype.setText=function(t){this.text_=t},t.prototype.setTextAlign=function(t){this.textAlign_=t},t.prototype.setTextBaseline=function(t){this.textBaseline_=t},t.prototype.setBackgroundFill=function(t){this.backgroundFill_=t},t.prototype.setBackgroundStroke=function(t){this.backgroundStroke_=t},t.prototype.setPadding=function(t){this.padding_=t},t}();function Yu(t,e){var r=/\{z\}/g,n=/\{x\}/g,i=/\{y\}/g,o=/\{-y\}/g;return function(a,s,u){return a?t.replace(r,a[0].toString()).replace(n,a[1].toString()).replace(i,a[2].toString()).replace(o,function(){var t=a[0],r=e.getFullTileRange(t);return K(r,55),(r.getHeight()-a[2]-1).toString()}):void 0}}function zu(t,e){for(var r=t.length,n=new Array(r),i=0;it)throw new Error("Tile load sequence violation");this.state=t,this.changed()},e.prototype.load=function(){n()},e.prototype.getAlpha=function(t,e){if(!this.transition_)return 1;var r=this.transitionStarts_[t];if(r){if(-1===r)return 1}else r=e,this.transitionStarts_[t]=r;var n=e-r+1e3/60;return n>=this.transition_?1:ti(n/this.transition_)},e.prototype.inTransition=function(t){return!!this.transition_&&-1!==this.transitionStarts_[t]},e.prototype.endTransition=function(t){this.transition_&&(this.transitionStarts_[t]=-1)},e}(A),Ju=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function Qu(){var t=li(1,1);return t.fillStyle="rgba(0,0,0,0)",t.fillRect(0,0,1,1),t.canvas}var $u=function(t){function e(e,r,n,i,o,a){var s=t.call(this,e,r,a)||this;return s.crossOrigin_=i,s.src_=n,s.image_=new Image,null!==i&&(s.image_.crossOrigin=i),s.unlisten_=null,s.tileLoadFunction_=o,s}return Ju(e,t),e.prototype.disposeInternal=function(){this.state==wn.LOADING&&(this.unlistenImage_(),this.image_=Qu()),this.interimTile&&this.interimTile.dispose(),t.prototype.disposeInternal.call(this)},e.prototype.getImage=function(){return this.image_},e.prototype.getKey=function(){return this.src_},e.prototype.handleImageError_=function(){this.state=wn.ERROR,this.unlistenImage_(),this.image_=Qu(),this.changed()},e.prototype.handleImageLoad_=function(){var t=this.image_;t.naturalWidth&&t.naturalHeight?this.state=wn.LOADED:this.state=wn.EMPTY,this.unlistenImage_(),this.changed()},e.prototype.load=function(){this.state==wn.ERROR&&(this.state=wn.IDLE,this.image_=new Image,null!==this.crossOrigin_&&(this.image_.crossOrigin=this.crossOrigin_)),this.state==wn.IDLE&&(this.state=wn.LOADING,this.changed(),this.tileLoadFunction_(this,this.src_),this.unlisten_=Ou(this.image_,this.handleImageLoad_.bind(this),this.handleImageError_.bind(this)))},e.prototype.unlistenImage_=function(){this.unlisten_&&(this.unlisten_(),this.unlisten_=null)},e}(qu),tl=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),el=function(t){function e(e){return t.call(this,e)||this}return tl(e,t),e.prototype.expireCache=function(t){for(;this.canExpireCache();){if(this.peekLast().getKey()in t)break;this.pop().dispose()}},e.prototype.pruneExceptNewestZ=function(){if(0!==this.getCount()){var t=function(t){return t.split("/").map(Number)}(this.peekFirstKey())[0];this.forEach(function(e){e.tileCoord[0]!==t&&(this.remove(fs(e.tileCoord)),e.dispose())}.bind(this))}},e}(_a);function rl(t,e,r,n){var i=Me(r,e,t),o=xe(e,n,r),a=e.getMetersPerUnit();void 0!==a&&(o*=a);var s=t.getMetersPerUnit();void 0!==s&&(o/=s);var u=t.getExtent();if(!u||nt(u,i)){var l=xe(t,o,i)/o;isFinite(l)&&l>0&&(o/=l)}return o}function nl(t,e,r,n){var i=r-t,o=n-e,a=Math.sqrt(i*i+o*o);return[Math.round(r+i/a),Math.round(n+o/a)]}function il(t,e,r,n,i,o,a,s,u,l,h){var c=li(Math.round(r*t),Math.round(r*e));if(0===u.length)return c.canvas;c.scale(r,r);var p=[1/0,1/0,-1/0,-1/0];u.forEach(function(t,e,r){ft(p,t.extent)});var f=It(p),d=Ot(p),_=li(Math.round(r*f/n),Math.round(r*d/n)),g=r/n;u.forEach(function(t,e,r){var n=t.extent[0]-p[0],i=-(t.extent[3]-p[3]),o=It(t.extent),a=Ot(t.extent);_.drawImage(t.image,l,l,t.image.width-2*l,t.image.height-2*l,n*g,i*g,o*g,a*g)});var y=Ct(a);return s.getTriangles().forEach(function(t,e,i){var a=t.source,s=t.target,u=a[0][0],l=a[0][1],h=a[1][0],f=a[1][1],d=a[2][0],g=a[2][1],v=(s[0][0]-y[0])/o,m=-(s[0][1]-y[1])/o,E=(s[1][0]-y[0])/o,T=-(s[1][1]-y[1])/o,S=(s[2][0]-y[0])/o,w=-(s[2][1]-y[1])/o,x=u,O=l;u=0,l=0;var R=function(t){for(var e=t.length,r=0;ri&&(i=a,n=o)}if(0===i)return null;var s=t[n];t[n]=t[r],t[r]=s;for(var u=r+1;u=0;p--){c[p]=t[p][e]/t[p][p];for(var f=p-1;f>=0;f--)t[f][e]-=t[f][p]*c[p]}return c}([[h-=x,f-=O,0,0,E-v],[d-=x,g-=O,0,0,S-v],[0,0,h,f,T-m],[0,0,d,g,w-m]]);if(R){c.save(),c.beginPath();var C=(v+E+S)/3,P=(m+T+w)/3,I=nl(C,P,v,m),b=nl(C,P,E,T),L=nl(C,P,S,w);c.moveTo(b[0],b[1]),c.lineTo(I[0],I[1]),c.lineTo(L[0],L[1]),c.clip(),c.transform(R[0],R[2],R[1],R[3],v,m),c.translate(p[0]-x,p[3]-O),c.scale(n/r,-n/r),c.drawImage(_.canvas,0,0),c.restore()}}),h&&(c.save(),c.strokeStyle="black",c.lineWidth=1,s.getTriangles().forEach(function(t,e,r){var n=t.target,i=(n[0][0]-y[0])/o,a=-(n[0][1]-y[1])/o,s=(n[1][0]-y[0])/o,u=-(n[1][1]-y[1])/o,l=(n[2][0]-y[0])/o,h=-(n[2][1]-y[1])/o;c.beginPath(),c.moveTo(s,u),c.lineTo(i,a),c.lineTo(l,h),c.closePath(),c.stroke()}),c.restore()),c.canvas}var ol=10,al=function(){function t(t,e,r,n,i){this.sourceProj_=t,this.targetProj_=e;var o={},a=Le(this.targetProj_,this.sourceProj_);this.transformInv_=function(t){var e=t[0]+"/"+t[1];return o[e]||(o[e]=a(t)),o[e]},this.maxSourceExtent_=n,this.errorThresholdSquared_=i*i,this.triangles_=[],this.wrapsXInSource_=!1,this.canWrapXInSource_=this.sourceProj_.canWrapX()&&!!n&&!!this.sourceProj_.getExtent()&&It(n)==It(this.sourceProj_.getExtent()),this.sourceWorldWidth_=this.sourceProj_.getExtent()?It(this.sourceProj_.getExtent()):null,this.targetWorldWidth_=this.targetProj_.getExtent()?It(this.targetProj_.getExtent()):null;var s=Ct(r),u=Pt(r),l=Tt(r),h=Et(r),c=this.transformInv_(s),p=this.transformInv_(u),f=this.transformInv_(l),d=this.transformInv_(h);if(this.addQuad_(s,u,l,h,c,p,f,d,ol),this.wrapsXInSource_){var _=1/0;this.triangles_.forEach(function(t,e,r){_=Math.min(_,t.source[0][0],t.source[1][0],t.source[2][0])}),this.triangles_.forEach(function(t){if(Math.max(t.source[0][0],t.source[1][0],t.source[2][0])-_>this.sourceWorldWidth_/2){var e=[[t.source[0][0],t.source[0][1]],[t.source[1][0],t.source[1][1]],[t.source[2][0],t.source[2][1]]];e[0][0]-_>this.sourceWorldWidth_/2&&(e[0][0]-=this.sourceWorldWidth_),e[1][0]-_>this.sourceWorldWidth_/2&&(e[1][0]-=this.sourceWorldWidth_),e[2][0]-_>this.sourceWorldWidth_/2&&(e[2][0]-=this.sourceWorldWidth_);var r=Math.min(e[0][0],e[1][0],e[2][0]);Math.max(e[0][0],e[1][0],e[2][0])-r.5&&h<1,f=!1;if(u>0){if(this.targetProj_.isGlobal()&&this.targetWorldWidth_)f=It($([t,e,r,n]))/this.targetWorldWidth_>.25||f;!p&&this.sourceProj_.isGlobal()&&h&&(f=h>.25||f)}if(f||!this.maxSourceExtent_||bt(l,this.maxSourceExtent_)){if(!(f||isFinite(i[0])&&isFinite(i[1])&&isFinite(o[0])&&isFinite(o[1])&&isFinite(a[0])&&isFinite(a[1])&&isFinite(s[0])&&isFinite(s[1]))){if(!(u>0))return;f=!0}if(u>0){if(!f){var d=[(t[0]+r[0])/2,(t[1]+r[1])/2],_=this.transformInv_(d),g=void 0;if(p)g=(Vt(i[0],c)+Vt(a[0],c))/2-Vt(_[0],c);else g=(i[0]+a[0])/2-_[0];var y=(i[1]+a[1])/2-_[1];f=g*g+y*y>this.errorThresholdSquared_}if(f){if(Math.abs(t[0]-r[0])<=Math.abs(t[1]-r[1])){var v=[(e[0]+r[0])/2,(e[1]+r[1])/2],m=this.transformInv_(v),E=[(n[0]+t[0])/2,(n[1]+t[1])/2],T=this.transformInv_(E);this.addQuad_(t,e,v,E,i,o,m,T,u-1),this.addQuad_(E,v,r,n,T,m,a,s,u-1)}else{var S=[(t[0]+e[0])/2,(t[1]+e[1])/2],w=this.transformInv_(S),x=[(r[0]+n[0])/2,(r[1]+n[1])/2],O=this.transformInv_(x);this.addQuad_(t,S,x,n,i,w,O,s,u-1),this.addQuad_(S,e,r,x,w,o,a,O,u-1)}return}}if(p){if(!this.canWrapXInSource_)return;this.wrapsXInSource_=!0}this.addTriangle_(t,r,n,i,a,s),this.addTriangle_(t,e,r,i,o,a)}},t.prototype.calculateSourceExtent=function(){var t=[1/0,1/0,-1/0,-1/0];return this.triangles_.forEach(function(e,r,n){var i=e.source;dt(t,i[0]),dt(t,i[1]),dt(t,i[2])}),t},t.prototype.getTriangles=function(){return this.triangles_},t}(),sl=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),ul=function(t){function e(e,r,n,i,o,a,s,u,l,h,c){var p=t.call(this,o,wn.IDLE)||this;p.renderEdges_=void 0!==c&&c,p.pixelRatio_=s,p.gutter_=u,p.canvas_=null,p.sourceTileGrid_=r,p.targetTileGrid_=i,p.wrappedTileCoord_=a||o,p.sourceTiles_=[],p.sourcesListenerKeys_=null,p.sourceZ_=0;var f=i.getTileCoordExtent(p.wrappedTileCoord_),d=p.targetTileGrid_.getExtent(),_=p.sourceTileGrid_.getExtent(),g=d?Rt(f,d):f;if(0===mt(g))return p.state=wn.EMPTY,p;var y=e.getExtent();y&&(_=_?Rt(_,y):y);var v=i.getResolution(p.wrappedTileCoord_[0]),m=rl(e,n,St(g),v);if(!isFinite(m)||m<=0)return p.state=wn.EMPTY,p;var E=void 0!==h?h:Ku;if(p.triangulation_=new al(e,n,g,_,m*E),0===p.triangulation_.getTriangles().length)return p.state=wn.EMPTY,p;p.sourceZ_=r.getZForResolution(m);var T=p.triangulation_.calculateSourceExtent();if(_&&(e.canWrapX()?(T[1]=kt(T[1],_[1],_[3]),T[3]=kt(T[3],_[1],_[3])):T=Rt(T,_)),mt(T)){for(var S=r.getTileRangeForExtentAndZ(T,p.sourceZ_),w=S.minX;w<=S.maxX;w++)for(var x=S.minY;x<=S.maxY;x++){var O=l(p.sourceZ_,w,x,s);O&&p.sourceTiles_.push(O)}0===p.sourceTiles_.length&&(p.state=wn.EMPTY)}else p.state=wn.EMPTY;return p}return sl(e,t),e.prototype.disposeInternal=function(){this.state==wn.LOADING&&this.unlistenSources_(),t.prototype.disposeInternal.call(this)},e.prototype.getImage=function(){return this.canvas_},e.prototype.reproject_=function(){var t=[];if(this.sourceTiles_.forEach(function(e,r,n){e&&e.getState()==wn.LOADED&&t.push({extent:this.sourceTileGrid_.getTileCoordExtent(e.tileCoord),image:e.getImage()})}.bind(this)),this.sourceTiles_.length=0,0===t.length)this.state=wn.ERROR;else{var e=this.wrappedTileCoord_[0],r=this.targetTileGrid_.getTileSize(e),n="number"==typeof r?r:r[0],i="number"==typeof r?r:r[1],o=this.targetTileGrid_.getResolution(e),a=this.sourceTileGrid_.getResolution(this.sourceZ_),s=this.targetTileGrid_.getTileCoordExtent(this.wrappedTileCoord_);this.canvas_=il(n,i,this.pixelRatio_,a,this.sourceTileGrid_.getExtent(),o,s,this.triangulation_,t,this.gutter_,this.renderEdges_),this.state=wn.LOADED}this.changed()},e.prototype.load=function(){if(this.state==wn.IDLE){this.state=wn.LOADING,this.changed();var t=0;this.sourcesListenerKeys_=[],this.sourceTiles_.forEach(function(e,r,n){var i=e.getState();if(i==wn.IDLE||i==wn.LOADING){t++;var o=g(e,N.CHANGE,function(r){var n=e.getState();n!=wn.LOADED&&n!=wn.ERROR&&n!=wn.EMPTY||(v(o),0===--t&&(this.unlistenSources_(),this.reproject_()))},this);this.sourcesListenerKeys_.push(o)}}.bind(this)),this.sourceTiles_.forEach(function(t,e,r){t.getState()==wn.IDLE&&t.load()}),0===t&&setTimeout(this.reproject_.bind(this),0)}},e.prototype.unlistenSources_=function(){this.sourcesListenerKeys_.forEach(v),this.sourcesListenerKeys_=null},e}(qu),ll=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function hl(t){return t?Array.isArray(t)?function(e){return t}:"function"==typeof t?t:function(e){return[t]}:null}var cl=function(t){function e(e){var r=t.call(this)||this;return r.projection_=we(e.projection),r.attributions_=hl(e.attributions),r.attributionsCollapsible_=void 0===e.attributionsCollapsible||e.attributionsCollapsible,r.loading=!1,r.state_=void 0!==e.state?e.state:vi.READY,r.wrapX_=void 0!==e.wrapX&&e.wrapX,r}return ll(e,t),e.prototype.getAttributions=function(){return this.attributions_},e.prototype.getAttributionsCollapsible=function(){return this.attributionsCollapsible_},e.prototype.getProjection=function(){return this.projection_},e.prototype.getResolutions=function(){return n()},e.prototype.getState=function(){return this.state_},e.prototype.getWrapX=function(){return this.wrapX_},e.prototype.refresh=function(){this.changed()},e.prototype.setAttributions=function(t){this.attributions_=hl(t),this.changed()},e.prototype.setState=function(t){this.state_=t,this.changed()},e}(z),pl=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),fl=function(t){function e(e){var r=t.call(this,{attributions:e.attributions,attributionsCollapsible:e.attributionsCollapsible,projection:e.projection,state:e.state,wrapX:e.wrapX})||this;r.opaque_=void 0!==e.opaque&&e.opaque,r.tilePixelRatio_=void 0!==e.tilePixelRatio?e.tilePixelRatio:1,r.tileGrid=void 0!==e.tileGrid?e.tileGrid:null;var n=e.cacheSize;if(void 0===n){var i=[256,256],o=e.tileGrid;o&&xi(o.getTileSize(o.getMinZoom()),i);var a="undefined"!=typeof screen,s=a?screen.availWidth||screen.width:1920,u=a?screen.availHeight||screen.height:1080;n=4*Math.ceil(s/i[0])*Math.ceil(u/i[1])}return r.tileCache=new el(n),r.tmpSize=[0,0],r.key_=e.key||"",r.tileOptions={transition:e.transition},r.zDirection=e.zDirection?e.zDirection:0,r}return pl(e,t),e.prototype.canExpireCache=function(){return this.tileCache.canExpireCache()},e.prototype.expireCache=function(t,e){var r=this.getTileCacheForProjection(t);r&&r.expireCache(e)},e.prototype.forEachLoadedTile=function(t,e,r,n){var i=this.getTileCacheForProjection(t);if(!i)return!1;for(var o,a,s,u=!0,l=r.minX;l<=r.maxX;++l)for(var h=r.minY;h<=r.maxY;++h)a=ps(e,l,h),s=!1,i.containsKey(a)&&(s=(o=i.get(a)).getState()===wn.LOADED)&&(s=!1!==n(o)),s||(u=!1);return u},e.prototype.getGutterForProjection=function(t){return 0},e.prototype.getKey=function(){return this.key_},e.prototype.setKey=function(t){this.key_!==t&&(this.key_=t,this.changed())},e.prototype.getOpaque=function(t){return this.opaque_},e.prototype.getResolutions=function(){return this.tileGrid.getResolutions()},e.prototype.getTile=function(t,e,r,i,o){return n()},e.prototype.getTileGrid=function(){return this.tileGrid},e.prototype.getTileGridForProjection=function(t){return this.tileGrid?this.tileGrid:ys(t)},e.prototype.getTileCacheForProjection=function(t){var e=this.getProjection();return e&&!Ie(e,t)?null:this.tileCache},e.prototype.getTilePixelRatio=function(t){return this.tilePixelRatio_},e.prototype.getTilePixelSize=function(t,e,r){var n=this.getTileGridForProjection(r),i=this.getTilePixelRatio(e),o=xi(n.getTileSize(t),this.tmpSize);return 1==i?o:wi(o,i,this.tmpSize)},e.prototype.getTileCoordForTileUrlFunction=function(t,e){var r=void 0!==e?e:this.getProjection(),n=this.getTileGridForProjection(r);return this.getWrapX()&&r.isGlobal()&&(t=function(t,e,r){var n=e[0],i=t.getTileCoordCenter(e),o=Ts(r);if(nt(o,i))return e;var a=It(o),s=Math.ceil((o[0]-i[0])/a);return i[0]+=a*s,t.getTileCoordForCoordAndZ(i,n)}(n,t,r)),function(t,e){var r=t[0],n=t[1],i=t[2];if(e.getMinZoom()>r||r>e.getMaxZoom())return!1;var o,a=e.getExtent();return!(o=a?e.getTileRangeForExtentAndZ(a,r):e.getFullTileRange(r))||o.containsXY(n,i)}(t,n)?t:null},e.prototype.clear=function(){this.tileCache.clear()},e.prototype.refresh=function(){this.clear(),t.prototype.refresh.call(this)},e.prototype.useTile=function(t,e,r,n){},e}(cl),dl=function(t){function e(e,r){var n=t.call(this,e)||this;return n.tile=r,n}return pl(e,t),e}(M),_l=fl,gl="tileloadstart",yl="tileloadend",vl="tileloaderror",ml=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),El=function(t){function e(e){var r=t.call(this,{attributions:e.attributions,cacheSize:e.cacheSize,opaque:e.opaque,projection:e.projection,state:e.state,tileGrid:e.tileGrid,tilePixelRatio:e.tilePixelRatio,wrapX:e.wrapX,transition:e.transition,key:e.key,attributionsCollapsible:e.attributionsCollapsible,zDirection:e.zDirection})||this;return r.generateTileUrlFunction_=!e.tileUrlFunction,r.tileLoadFunction=e.tileLoadFunction,r.tileUrlFunction=e.tileUrlFunction?e.tileUrlFunction.bind(r):Vu,r.urls=null,e.urls?r.setUrls(e.urls):e.url&&r.setUrl(e.url),r.tileLoadingKeys_={},r}return ml(e,t),e.prototype.getTileLoadFunction=function(){return this.tileLoadFunction},e.prototype.getTileUrlFunction=function(){return this.tileUrlFunction},e.prototype.getUrls=function(){return this.urls},e.prototype.handleTileChange=function(t){var e,r=t.target,n=o(r),i=r.getState();i==wn.LOADING?(this.tileLoadingKeys_[n]=!0,e=gl):n in this.tileLoadingKeys_&&(delete this.tileLoadingKeys_[n],e=i==wn.ERROR?vl:i==wn.LOADED||i==wn.ABORT?yl:void 0),null!=e&&this.dispatchEvent(new dl(e,r))},e.prototype.setTileLoadFunction=function(t){this.tileCache.clear(),this.tileLoadFunction=t,this.changed()},e.prototype.setTileUrlFunction=function(t,e){this.tileUrlFunction=t,this.tileCache.pruneExceptNewestZ(),void 0!==e?this.setKey(e):this.changed()},e.prototype.setUrl=function(t){var e=Wu(t);this.urls=e,this.setUrls(e)},e.prototype.setUrls=function(t){this.urls=t;var e=t.join("\n");this.generateTileUrlFunction_?this.setTileUrlFunction(zu(t,this.tileGrid),e):this.setKey(e)},e.prototype.useTile=function(t,e,r){var n=ps(t,e,r);this.tileCache.containsKey(n)&&this.tileCache.get(n)},e}(_l),Tl=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function Sl(t,e){t.getImage().src=e}var wl=function(t){function e(e){var r=t.call(this,{attributions:e.attributions,cacheSize:e.cacheSize,opaque:e.opaque,projection:e.projection,state:e.state,tileGrid:e.tileGrid,tileLoadFunction:e.tileLoadFunction?e.tileLoadFunction:Sl,tilePixelRatio:e.tilePixelRatio,tileUrlFunction:e.tileUrlFunction,url:e.url,urls:e.urls,wrapX:e.wrapX,transition:e.transition,key:e.key,attributionsCollapsible:e.attributionsCollapsible,zDirection:e.zDirection})||this;return r.crossOrigin=void 0!==e.crossOrigin?e.crossOrigin:null,r.tileClass=void 0!==e.tileClass?e.tileClass:$u,r.tileCacheForProjection={},r.tileGridForProjection={},r.reprojectionErrorThreshold_=e.reprojectionErrorThreshold,r.renderReprojectionEdges_=!1,r}return Tl(e,t),e.prototype.canExpireCache=function(){if(this.tileCache.canExpireCache())return!0;for(var t in this.tileCacheForProjection)if(this.tileCacheForProjection[t].canExpireCache())return!0;return!1},e.prototype.expireCache=function(t,e){var r=this.getTileCacheForProjection(t);for(var n in this.tileCache.expireCache(this.tileCache==r?e:{}),this.tileCacheForProjection){var i=this.tileCacheForProjection[n];i.expireCache(i==r?e:{})}},e.prototype.getGutterForProjection=function(t){return this.getProjection()&&t&&!Ie(this.getProjection(),t)?0:this.getGutter()},e.prototype.getGutter=function(){return 0},e.prototype.getOpaque=function(e){return!(this.getProjection()&&e&&!Ie(this.getProjection(),e))&&t.prototype.getOpaque.call(this,e)},e.prototype.getTileGridForProjection=function(t){var e=this.getProjection();if(!this.tileGrid||e&&!Ie(e,t)){var r=o(t);return r in this.tileGridForProjection||(this.tileGridForProjection[r]=ys(t)),this.tileGridForProjection[r]}return this.tileGrid},e.prototype.getTileCacheForProjection=function(t){var e=this.getProjection();if(!e||Ie(e,t))return this.tileCache;var r=o(t);return r in this.tileCacheForProjection||(this.tileCacheForProjection[r]=new el(this.tileCache.highWaterMark)),this.tileCacheForProjection[r]},e.prototype.createTile_=function(t,e,r,n,i,o){var a=[t,e,r],s=this.getTileCoordForTileUrlFunction(a,i),u=s?this.tileUrlFunction(s,n,i):void 0,l=new this.tileClass(a,void 0!==u?wn.IDLE:wn.EMPTY,void 0!==u?u:"",this.crossOrigin,this.tileLoadFunction,this.tileOptions);return l.key=o,l.addEventListener(N.CHANGE,this.handleTileChange.bind(this)),l},e.prototype.getTile=function(t,e,r,n,i){var o=this.getProjection();if(o&&i&&!Ie(o,i)){var a=this.getTileCacheForProjection(i),s=[t,e,r],u=void 0,l=fs(s);a.containsKey(l)&&(u=a.get(l));var h=this.getKey();if(u&&u.key==h)return u;var c=this.getTileGridForProjection(o),p=this.getTileGridForProjection(i),f=this.getTileCoordForTileUrlFunction(s,i),d=new ul(o,c,i,p,s,f,this.getTilePixelRatio(n),this.getGutter(),function(t,e,r,n){return this.getTileInternal(t,e,r,n,o)}.bind(this),this.reprojectionErrorThreshold_,this.renderReprojectionEdges_);return d.key=h,u?(d.interimTile=u,d.refreshInterimChain(),a.replace(l,d)):a.set(l,d),d}return this.getTileInternal(t,e,r,n,o||i)},e.prototype.getTileInternal=function(t,e,r,n,i){var o=null,a=ps(t,e,r),s=this.getKey();if(this.tileCache.containsKey(a)){if((o=this.tileCache.get(a)).key!=s){var u=o;o=this.createTile_(t,e,r,n,i,s),u.getState()==wn.IDLE?o.interimTile=u.interimTile:o.interimTile=u,o.refreshInterimChain(),this.tileCache.replace(a,o)}}else o=this.createTile_(t,e,r,n,i,s),this.tileCache.set(a,o);return o},e.prototype.setRenderReprojectionEdges=function(t){if(this.renderReprojectionEdges_!=t){for(var e in this.renderReprojectionEdges_=t,this.tileCacheForProjection)this.tileCacheForProjection[e].clear();this.changed()}},e.prototype.setTileGridForProjection=function(t,e){var r=we(t);if(r){var n=o(r);n in this.tileGridForProjection||(this.tileGridForProjection[n]=e)}},e}(El),xl=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();var Ol=function(t){function e(e){var r=this,n=void 0!==e.hidpi&&e.hidpi;return(r=t.call(this,{cacheSize:e.cacheSize,crossOrigin:"anonymous",opaque:!0,projection:we("EPSG:3857"),reprojectionErrorThreshold:e.reprojectionErrorThreshold,state:vi.LOADING,tileLoadFunction:e.tileLoadFunction,tilePixelRatio:n?2:1,wrapX:void 0===e.wrapX||e.wrapX,transition:e.transition})||this).hidpi_=n,r.culture_=void 0!==e.culture?e.culture:"en-us",r.maxZoom_=void 0!==e.maxZoom?e.maxZoom:-1,r.apiKey_=e.key,r.imagerySet_=e.imagerySet,Zu("https://dev.virtualearth.net/REST/v1/Imagery/Metadata/"+r.imagerySet_+"?uriScheme=https&include=ImageryProviders&key="+r.apiKey_+"&c="+r.culture_,r.handleImageryMetadataResponse.bind(r),void 0,"jsonp"),r}return xl(e,t),e.prototype.getApiKey=function(){return this.apiKey_},e.prototype.getImagerySet=function(){return this.imagerySet_},e.prototype.handleImageryMetadataResponse=function(t){if(200==t.statusCode&&"OK"==t.statusDescription&&"ValidCredentials"==t.authenticationResultCode&&1==t.resourceSets.length&&1==t.resourceSets[0].resources.length){var e=t.resourceSets[0].resources[0],r=-1==this.maxZoom_?e.zoomMax:this.maxZoom_,n=Ts(this.getProjection()),i=this.hidpi_?2:1,o=e.imageWidth==e.imageHeight?e.imageWidth/i:[e.imageWidth/i,e.imageHeight/i],a=vs({extent:n,minZoom:e.zoomMin,maxZoom:r,tileSize:o});this.tileGrid=a;var s=this.culture_,u=this.hidpi_;if(this.tileUrlFunction=Xu(e.imageUrlSubdomains.map(function(t){var r=[0,0,0],n=e.imageUrl.replace("{subdomain}",t).replace("{culture}",s);return function(t,e,i){if(t){cs(t[0],t[1],t[2],r);var o=n;return u&&(o+="&dpi=d1&device=mobile"),o.replace("{quadkey}",function(t){var e,r,n=t[0],i=new Array(n),o=1<>=1;return i.join("")}(r))}}})),e.imageryProviders){var l=be(we("EPSG:4326"),this.getProjection());this.setAttributions(function(t){var r=[],n=t.viewState,i=this.getTileGrid(),o=i.getZForResolution(n.resolution,this.zDirection),a=i.getTileCoordForCoordAndZ(n.center,o)[0];return e.imageryProviders.map(function(e){for(var n=!1,i=e.coverageAreas,o=0,s=i.length;o=u.zoomMin&&a<=u.zoomMax){var h=u.bbox;if(bt(Ft([h[1],h[0],h[3],h[2]],l),t.extent)){n=!0;break}}}n&&r.push(e.attribution)}),r.push('Terms of Use'),r}.bind(this))}this.setState(vi.READY)}else this.setState(vi.ERROR)},e}(wl),Rl=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Cl=function(t){function e(e){var r=e||{},n=void 0!==r.projection?r.projection:"EPSG:3857",i=void 0!==r.tileGrid?r.tileGrid:vs({extent:Ts(n),maxZoom:r.maxZoom,minZoom:r.minZoom,tileSize:r.tileSize});return t.call(this,{attributions:r.attributions,cacheSize:r.cacheSize,crossOrigin:r.crossOrigin,opaque:r.opaque,projection:n,reprojectionErrorThreshold:r.reprojectionErrorThreshold,tileGrid:i,tileLoadFunction:r.tileLoadFunction,tilePixelRatio:r.tilePixelRatio,tileUrlFunction:r.tileUrlFunction,url:r.url,urls:r.urls,wrapX:void 0===r.wrapX||r.wrapX,transition:r.transition,attributionsCollapsible:r.attributionsCollapsible,zDirection:r.zDirection})||this}return Rl(e,t),e}(wl),Pl=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Il=function(t){function e(e){var r=t.call(this,{attributions:e.attributions,cacheSize:e.cacheSize,crossOrigin:e.crossOrigin,maxZoom:void 0!==e.maxZoom?e.maxZoom:18,minZoom:e.minZoom,projection:e.projection,wrapX:e.wrapX})||this;return r.account_=e.account,r.mapId_=e.map||"",r.config_=e.config||{},r.templateCache_={},r.initializeMap_(),r}return Pl(e,t),e.prototype.getConfig=function(){return this.config_},e.prototype.updateConfig=function(t){p(this.config_,t),this.initializeMap_()},e.prototype.setConfig=function(t){this.config_=t||{},this.initializeMap_()},e.prototype.initializeMap_=function(){var t=JSON.stringify(this.config_);if(this.templateCache_[t])this.applyTemplate_(this.templateCache_[t]);else{var e="https://"+this.account_+".carto.com/api/v1/map";this.mapId_&&(e+="/named/"+this.mapId_);var r=new XMLHttpRequest;r.addEventListener("load",this.handleInitResponse_.bind(this,t)),r.addEventListener("error",this.handleInitError_.bind(this)),r.open("POST",e),r.setRequestHeader("Content-type","application/json"),r.send(JSON.stringify(this.config_))}},e.prototype.handleInitResponse_=function(t,e){var r=e.target;if(!r.status||r.status>=200&&r.status<300){var n=void 0;try{n=JSON.parse(r.responseText)}catch(t){return void this.setState(vi.ERROR)}this.applyTemplate_(n),this.templateCache_[t]=n,this.setState(vi.READY)}else this.setState(vi.ERROR)},e.prototype.handleInitError_=function(t){this.setState(vi.ERROR)},e.prototype.applyTemplate_=function(t){var e="https://"+t.cdn_url.https+"/"+this.account_+"/api/v1/map/"+t.layergroupid+"/{z}/{x}/{y}.png";this.setUrl(e)},e}(Cl),bl={ADDFEATURE:"addfeature",CHANGEFEATURE:"changefeature",CLEAR:"clear",REMOVEFEATURE:"removefeature"},Ll=r(0),Ml=r.n(Ll),Fl=function(){function t(t){this.rbush_=new Ml.a(t),this.items_={}}return t.prototype.insert=function(t,e){var r={minX:t[0],minY:t[1],maxX:t[2],maxY:t[3],value:e};this.rbush_.insert(r),this.items_[o(e)]=r},t.prototype.load=function(t,e){for(var r=new Array(e.length),n=0,i=e.length;n=0;--r){var n=this.geometryFunction(t[r]);n?zn(e,n.getCoordinates()):t.splice(r,1)}Hn(e,1/t.length);var i=new q(new Or(e));return i.set("features",t),i},e}(Dl),kl=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Ul={DEFAULT:"default",TRUNCATED:"truncated"},Bl=function(t){function e(e,r,n,i,o,a,s,u){var l=t.call(this,n,i,o,a,s,u)||this;return l.zoomifyImage_=null,l.tileSize_=xi(r.getTileSize(n[0])).map(function(t){return t*e}),l}return kl(e,t),e.prototype.getImage=function(){if(this.zoomifyImage_)return this.zoomifyImage_;var e=t.prototype.getImage.call(this);if(this.state==wn.LOADED){var r=this.tileSize_;if(e.width==r[0]&&e.height==r[1])return this.zoomifyImage_=e,e;var n=li(r[0],r[1]);return n.drawImage(e,0,0),this.zoomifyImage_=n.canvas,n.canvas}return e},e}($u),Yl=function(t){function e(e){var r=this,n=e||{},i=n.size,o=void 0!==n.tierSizeCalculation?n.tierSizeCalculation:Ul.DEFAULT,a=i[0],s=i[1],u=n.extent||[0,-i[1],i[0],0],l=[],h=n.tileSize||Pn,c=n.tilePixelRatio||1,p=h;switch(o){case Ul.DEFAULT:for(;a>p||s>p;)l.push([Math.ceil(a/p),Math.ceil(s/p)]),p+=p;break;case Ul.TRUNCATED:for(var f=a,d=s;f>p||d>p;)l.push([Math.ceil(f/p),Math.ceil(d/p)]),f>>=1,d>>=1;break;default:K(!1,53)}l.push([1,1]),l.reverse();for(var _=[1],g=[0],y=1,v=l.length;y1,n=r&&t.imageInfo.profile[1].supports?t.imageInfo.profile[1].supports:[],i=r&&t.imageInfo.profile[1].formats?t.imageInfo.profile[1].formats:[],o=r&&t.imageInfo.profile[1].qualities?t.imageInfo.profile[1].qualities:[];return{url:t.imageInfo["@id"].replace(/\/?(info.json)?$/g,""),sizes:void 0===t.imageInfo.sizes?void 0:t.imageInfo.sizes.map(function(t){return[t.width,t.height]}),tileSize:void 0===t.imageInfo.tiles?void 0:[t.imageInfo.tiles.map(function(t){return t.width})[0],t.imageInfo.tiles.map(function(t){return void 0===t.height?t.width:t.height})[0]],resolutions:void 0===t.imageInfo.tiles?void 0:t.imageInfo.tiles.map(function(t){return t.scaleFactors})[0],supports:e.supports.concat(n),formats:e.formats.concat(i),qualities:e.qualities.concat(o)}},Kl[zl.VERSION3]=function(t){var e=t.getComplianceLevelSupportedFeatures(),r=void 0===t.imageInfo.extraFormats?e.formats:e.formats.concat(t.imageInfo.extraFormats),n=void 0!==t.imageInfo.preferredFormats&&Array.isArray(t.imageInfo.preferredFormats)&&t.imageInfo.preferredFormats.length>0?t.imageInfo.preferredFormats.filter(function(t){return["jpg","png","gif"].includes(t)}).reduce(function(t,e){return void 0===t&&r.includes(e)?e:t},void 0):void 0;return{url:t.imageInfo.id,sizes:void 0===t.imageInfo.sizes?void 0:t.imageInfo.sizes.map(function(t){return[t.width,t.height]}),tileSize:void 0===t.imageInfo.tiles?void 0:[t.imageInfo.tiles.map(function(t){return t.width})[0],t.imageInfo.tiles.map(function(t){return t.height})[0]],resolutions:void 0===t.imageInfo.tiles?void 0:t.imageInfo.tiles.map(function(t){return t.scaleFactors})[0],supports:void 0===t.imageInfo.extraFeatures?e.supports:e.supports.concat(t.imageInfo.extraFeatures),formats:r,qualities:void 0===t.imageInfo.extraQualities?e.qualities:e.qualities.concat(t.imageInfo.extraQualities),preferredFormat:n}};var Hl=function(){function t(t){this.setImageInfo(t)}return t.prototype.setImageInfo=function(t){this.imageInfo="string"==typeof t?JSON.parse(t):t},t.prototype.getImageApiVersion=function(){if(void 0!==this.imageInfo){var t=this.imageInfo["@context"]||"ol-no-context";"string"==typeof t&&(t=[t]);for(var e=0;e0&&"string"==typeof this.imageInfo.profile[0]&&Wl.test(this.imageInfo.profile[0]))return this.imageInfo.profile[0]}},t.prototype.getComplianceLevelFromProfile=function(t){var e=this.getComplianceLevelEntryFromProfile(t);if(void 0!==e){var r=e.match(/level[0-2](\.json)?$/g);return Array.isArray(r)?r[0].replace(".json",""):void 0}},t.prototype.getComplianceLevelSupportedFeatures=function(){if(void 0!==this.imageInfo){var t=this.getImageApiVersion(),e=this.getComplianceLevelFromProfile(t);return void 0===e?Xl.none.none:Xl[t][e]}},t.prototype.getTileSourceOptions=function(t){var e=t||{},r=this.getImageApiVersion();if(void 0!==r){var n=void 0===r?void 0:Kl[r](this);if(void 0!==n)return{url:n.url,version:r,size:[this.imageInfo.width,this.imageInfo.height],sizes:n.sizes,format:void 0!==e.format&&n.formats.includes(e.format)?e.format:void 0!==n.preferredFormat?n.preferredFormat:"jpg",supports:n.supports,quality:e.quality&&n.qualities.includes(e.quality)?e.quality:n.qualities.includes("native")?"native":"default",resolutions:Array.isArray(n.resolutions)?n.resolutions.sort(function(t,e){return e-t}):void 0,tileSize:n.tileSize}}},t}(),ql=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function Jl(t){return t.toLocaleString("en",{maximumFractionDigits:10})}var Ql=function(t){function e(e){var r=this,n=e||{},i=n.url||"";i+=i.lastIndexOf("/")===i.length-1||""===i?"":"/";var o=n.version||zl.VERSION2,a=n.sizes||[],s=n.size;K(null!=s&&Array.isArray(s)&&2==s.length&&!isNaN(s[0])&&s[0]>0&&!isNaN(s[1])&&s[1]>0,60);var u,l,h,c=s[0],p=s[1],f=n.tileSize,d=n.tilePixelRatio||1,_=n.format||"jpg",g=n.quality||(n.version==zl.VERSION1?"native":"default"),y=n.resolutions||[],v=n.supports||[],m=n.extent||[0,-p,c,0],E=null!=a&&Array.isArray(a)&&a.length>0,T=null!=f&&("number"==typeof f&&Number.isInteger(f)&&f>0||Array.isArray(f)&&f.length>0),S=null!=v&&Array.isArray(v)&&(v.includes("regionByPx")||v.includes("regionByPct"))&&(v.includes("sizeByWh")||v.includes("sizeByH")||v.includes("sizeByW")||v.includes("sizeByPct"));if(y.sort(function(t,e){return e-t}),T||S)if(null!=f&&("number"==typeof f&&Number.isInteger(f)&&f>0?(u=f,l=f):Array.isArray(f)&&f.length>0&&((1==f.length||null==f[1]&&Number.isInteger(f[0]))&&(u=f[0],l=f[0]),2==f.length&&(Number.isInteger(f[0])&&Number.isInteger(f[1])?(u=f[0],l=f[1]):null==f[0]&&Number.isInteger(f[1])&&(u=f[1],l=f[1])))),void 0!==u&&void 0!==l||(u=Pn,l=Pn),0==y.length)for(var w=h=Math.max(Math.ceil(Math.log(c/u)/Math.LN2),Math.ceil(Math.log(p/l)/Math.LN2));w>=0;w--)y.push(Math.pow(2,w));else{var x=Math.max.apply(Math,y);h=Math.round(Math.log(x)/Math.LN2)}else if(u=c,l=p,y=[],E){a.sort(function(t,e){return t[0]-e[0]}),h=-1;var O=[];for(w=0;w0&&y[y.length-1]==R?O.push(w):(y.push(R),h++)}if(O.length>0)for(w=0;wh)){var d=t[1],m=t[2],w=y[f];if(!(void 0===d||void 0===m||void 0===w||d<0||Math.ceil(c/w/u)<=d||m<0||Math.ceil(p/w/l)<=m)){if(S||T){var x=d*u*w,O=m*l*w,R=u*w,C=l*w,P=u,I=l;if(x+R>c&&(R=c-x),O+C>p&&(C=p-O),x+u*w>c&&(P=Math.floor((c-x+w-1)/w)),O+l*w>p&&(I=Math.floor((p-O+w-1)/w)),0==x&&R==c&&0==O&&C==p)n="full";else if(!S||v.includes("regionByPx"))n=x+","+O+","+R+","+C;else if(v.includes("regionByPct")){n="pct:"+Jl(x/c*100)+","+Jl(O/p*100)+","+Jl(R/c*100)+","+Jl(C/p*100)}o!=zl.VERSION3||S&&!v.includes("sizeByWh")?!S||v.includes("sizeByW")?s=P+",":v.includes("sizeByH")?s=","+I:v.includes("sizeByWh")?s=P+","+I:v.includes("sizeByPct")&&(s="pct:"+Jl(100/w)):s=P+","+I}else if(n="full",E){var b=a[f][0],L=a[f][1];s=o==zl.VERSION3?b==c&&L==p?"max":b+","+L:b==c?"full":b+","}else s=o==zl.VERSION3?"max":"full";return i+n+"/"+s+"/0/"+g+"."+_}}},transition:n.transition})||this).zDirection=n.zDirection,r}return ql(e,t),e}(wl),$l=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),th=function(t){function e(e,r,n,i,o,a){var s=this,u=e.getExtent(),l=r.getExtent(),h=l?Rt(n,l):n,c=rl(e,r,St(h),i),p=new al(e,r,h,u,c*Ku),f=a(p.calculateSourceExtent(),c,o),d=f?Da.IDLE:Da.EMPTY,_=f?f.getPixelRatio():1;return(s=t.call(this,n,i,_,d)||this).targetProj_=r,s.maxSourceExtent_=u,s.triangulation_=p,s.targetResolution_=i,s.targetExtent_=n,s.sourceImage_=f,s.sourcePixelRatio_=_,s.canvas_=null,s.sourceListenerKey_=null,s}return $l(e,t),e.prototype.disposeInternal=function(){this.state==Da.LOADING&&this.unlistenSource_(),t.prototype.disposeInternal.call(this)},e.prototype.getImage=function(){return this.canvas_},e.prototype.getProjection=function(){return this.targetProj_},e.prototype.reproject_=function(){var t=this.sourceImage_.getState();if(t==Da.LOADED){var e=It(this.targetExtent_)/this.targetResolution_,r=Ot(this.targetExtent_)/this.targetResolution_;this.canvas_=il(e,r,this.sourcePixelRatio_,this.sourceImage_.getResolution(),this.maxSourceExtent_,this.targetResolution_,this.targetExtent_,this.triangulation_,[{extent:this.sourceImage_.getExtent(),image:this.sourceImage_.getImage()}],0)}this.state=t,this.changed()},e.prototype.load=function(){if(this.state==Da.IDLE){this.state=Da.LOADING,this.changed();var t=this.sourceImage_.getState();t==Da.LOADED||t==Da.ERROR?this.reproject_():(this.sourceListenerKey_=g(this.sourceImage_,N.CHANGE,function(t){var e=this.sourceImage_.getState();e!=Da.LOADED&&e!=Da.ERROR||(this.unlistenSource_(),this.reproject_())},this),this.sourceImage_.load())}},e.prototype.unlistenSource_=function(){v(this.sourceListenerKey_),this.sourceListenerKey_=null},e}(wu),eh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),rh="imageloadstart",nh="imageloadend",ih="imageloaderror",oh=function(t){function e(e,r){var n=t.call(this,e)||this;return n.image=r,n}return eh(e,t),e}(M);function ah(t,e){t.getImage().src=e}var sh=function(t){function e(e){var r=t.call(this,{attributions:e.attributions,projection:e.projection,state:e.state})||this;return r.resolutions_=void 0!==e.resolutions?e.resolutions:null,r.reprojectedImage_=null,r.reprojectedRevision_=0,r}return eh(e,t),e.prototype.getResolutions=function(){return this.resolutions_},e.prototype.findNearestResolution=function(t){if(this.resolutions_){var e=S(this.resolutions_,t,0);t=this.resolutions_[e]}return t},e.prototype.getImage=function(t,e,r,n){var i=this.getProjection();if(i&&n&&!Ie(i,n)){if(this.reprojectedImage_){if(this.reprojectedRevision_==this.getRevision()&&Ie(this.reprojectedImage_.getProjection(),n)&&this.reprojectedImage_.getResolution()==e&&pt(this.reprojectedImage_.getExtent(),t))return this.reprojectedImage_;this.reprojectedImage_.dispose(),this.reprojectedImage_=null}return this.reprojectedImage_=new th(i,n,t,e,r,function(t,e,r){return this.getImageInternal(t,e,r,i)}.bind(this)),this.reprojectedRevision_=this.getRevision(),this.reprojectedImage_}return i&&(n=i),this.getImageInternal(t,e,r,n)},e.prototype.getImageInternal=function(t,e,r,i){return n()},e.prototype.handleImageChange=function(t){var e=t.target;switch(e.getState()){case Da.LOADING:this.loading=!0,this.dispatchEvent(new oh(rh,e));break;case Da.LOADED:this.loading=!1,this.dispatchEvent(new oh(nh,e));break;case Da.ERROR:this.loading=!1,this.dispatchEvent(new oh(ih,e))}},e}(cl);function uh(t,e){var r=[];Object.keys(e).forEach(function(t){null!==e[t]&&void 0!==e[t]&&r.push(t+"="+encodeURIComponent(e[t]))});var n=r.join("&");return(t=-1===(t=t.replace(/[?&]$/,"")).indexOf("?")?t+"?":t+"&")+n}var lh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),hh=function(t){function e(e){var r=this,n=e||{};return(r=t.call(this,{attributions:n.attributions,projection:n.projection,resolutions:n.resolutions})||this).crossOrigin_=void 0!==n.crossOrigin?n.crossOrigin:null,r.hidpi_=void 0===n.hidpi||n.hidpi,r.url_=n.url,r.imageLoadFunction_=void 0!==n.imageLoadFunction?n.imageLoadFunction:ah,r.params_=n.params||{},r.image_=null,r.imageSize_=[0,0],r.renderedRevision_=0,r.ratio_=void 0!==n.ratio?n.ratio:1.5,r}return lh(e,t),e.prototype.getParams=function(){return this.params_},e.prototype.getImageInternal=function(t,e,r,n){if(void 0===this.url_)return null;e=this.findNearestResolution(e),r=this.hidpi_?r:1;var i=this.image_;if(i&&this.renderedRevision_==this.getRevision()&&i.getResolution()==e&&i.getPixelRatio()==r&&it(i.getExtent(),t))return i;var o={F:"image",FORMAT:"PNG32",TRANSPARENT:!0};p(o,this.params_);var a=((t=t.slice())[0]+t[2])/2,s=(t[1]+t[3])/2;if(1!=this.ratio_){var u=this.ratio_*It(t)/2,l=this.ratio_*Ot(t)/2;t[0]=a-u,t[1]=s-l,t[2]=a+u,t[3]=s+l}var h=e/r,c=Math.ceil(It(t)/h),f=Math.ceil(Ot(t)/h);t[0]=a-h*c/2,t[2]=a+h*c/2,t[1]=s-h*f/2,t[3]=s+h*f/2,this.imageSize_[0]=c,this.imageSize_[1]=f;var d=this.getRequestUrl_(t,this.imageSize_,r,n,o);return this.image_=new Ru(t,e,r,d,this.crossOrigin_,this.imageLoadFunction_),this.renderedRevision_=this.getRevision(),this.image_.addEventListener(N.CHANGE,this.handleImageChange.bind(this)),this.image_},e.prototype.getImageLoadFunction=function(){return this.imageLoadFunction_},e.prototype.getRequestUrl_=function(t,e,r,n,i){var o=n.getCode().split(":").pop();i.SIZE=e[0]+","+e[1],i.BBOX=t.join(","),i.BBOXSR=o,i.IMAGESR=o,i.DPI=Math.round(90*r);var a=this.url_,s=a.replace(/MapServer\/?$/,"MapServer/export").replace(/ImageServer\/?$/,"ImageServer/exportImage");return s==a&&K(!1,50),uh(s,i)},e.prototype.getUrl=function(){return this.url_},e.prototype.setImageLoadFunction=function(t){this.image_=null,this.imageLoadFunction_=t,this.changed()},e.prototype.setUrl=function(t){t!=this.url_&&(this.url_=t,this.image_=null,this.changed())},e.prototype.updateParams=function(t){p(this.params_,t),this.image_=null,this.changed()},e}(sh),ch=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),ph=function(t){function e(e,r,n,i,o){var a=this,s=void 0!==o?Da.IDLE:Da.LOADED;return(a=t.call(this,e,r,n,s)||this).loader_=void 0!==o?o:null,a.canvas_=i,a.error_=null,a}return ch(e,t),e.prototype.getError=function(){return this.error_},e.prototype.handleLoad_=function(t){t?(this.error_=t,this.state=Da.ERROR):this.state=Da.LOADED,this.changed()},e.prototype.load=function(){this.state==Da.IDLE&&(this.state=Da.LOADING,this.changed(),this.loader_(this.handleLoad_.bind(this)))},e.prototype.getImage=function(){return this.canvas_},e}(wu),fh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),dh=function(t){function e(e){var r=this,n=e||{};return(r=t.call(this,{attributions:n.attributions,projection:n.projection,resolutions:n.resolutions,state:n.state})||this).canvasFunction_=n.canvasFunction,r.canvas_=null,r.renderedRevision_=0,r.ratio_=void 0!==n.ratio?n.ratio:1.5,r}return fh(e,t),e.prototype.getImageInternal=function(t,e,r,n){e=this.findNearestResolution(e);var i=this.canvas_;if(i&&this.renderedRevision_==this.getRevision()&&i.getResolution()==e&&i.getPixelRatio()==r&&it(i.getExtent(),t))return i;Mt(t=t.slice(),this.ratio_);var o=[It(t)/e*r,Ot(t)/e*r],a=this.canvasFunction_.call(this,t,e,r,o,n);return a&&(i=new ph(t,e,r,a)),this.canvas_=i,this.renderedRevision_=this.getRevision(),i},e}(sh),_h=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();var gh=function(t){function e(e){var r=t.call(this,{projection:e.projection,resolutions:e.resolutions})||this;return r.crossOrigin_=void 0!==e.crossOrigin?e.crossOrigin:null,r.displayDpi_=void 0!==e.displayDpi?e.displayDpi:96,r.params_=e.params||{},r.url_=e.url,r.imageLoadFunction_=void 0!==e.imageLoadFunction?e.imageLoadFunction:ah,r.hidpi_=void 0===e.hidpi||e.hidpi,r.metersPerUnit_=void 0!==e.metersPerUnit?e.metersPerUnit:1,r.ratio_=void 0!==e.ratio?e.ratio:1,r.useOverlay_=void 0!==e.useOverlay&&e.useOverlay,r.image_=null,r.renderedRevision_=0,r}return _h(e,t),e.prototype.getParams=function(){return this.params_},e.prototype.getImageInternal=function(t,e,r,n){e=this.findNearestResolution(e),r=this.hidpi_?r:1;var i=this.image_;if(i&&this.renderedRevision_==this.getRevision()&&i.getResolution()==e&&i.getPixelRatio()==r&&it(i.getExtent(),t))return i;1!=this.ratio_&&Mt(t=t.slice(),this.ratio_);var o=[It(t)/e*r,Ot(t)/e*r];if(void 0!==this.url_){var a=this.getUrl(this.url_,this.params_,t,o,n);(i=new Ru(t,e,r,a,this.crossOrigin_,this.imageLoadFunction_)).addEventListener(N.CHANGE,this.handleImageChange.bind(this))}else i=null;return this.image_=i,this.renderedRevision_=this.getRevision(),i},e.prototype.getImageLoadFunction=function(){return this.imageLoadFunction_},e.prototype.updateParams=function(t){p(this.params_,t),this.changed()},e.prototype.getUrl=function(t,e,r,n,i){var o=function(t,e,r,n){var i=It(t),o=Ot(t),a=e[0],s=e[1],u=.0254/n;return s*i>a*o?i*r/(a*u):o*r/(s*u)}(r,n,this.metersPerUnit_,this.displayDpi_),a=St(r),s={OPERATION:this.useOverlay_?"GETDYNAMICMAPOVERLAYIMAGE":"GETMAPIMAGE",VERSION:"2.0.0",LOCALE:"en",CLIENTAGENT:"ol/source/ImageMapGuide source",CLIP:"1",SETDISPLAYDPI:this.displayDpi_,SETDISPLAYWIDTH:Math.round(n[0]),SETDISPLAYHEIGHT:Math.round(n[1]),SETVIEWSCALE:o,SETVIEWCENTERX:a[0],SETVIEWCENTERY:a[1]};return p(s,e),uh(t,s)},e.prototype.setImageLoadFunction=function(t){this.image_=null,this.imageLoadFunction_=t,this.changed()},e}(sh),yh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),vh=function(t){function e(e){var r=this,n=void 0!==e.crossOrigin?e.crossOrigin:null,i=void 0!==e.imageLoadFunction?e.imageLoadFunction:ah;return(r=t.call(this,{attributions:e.attributions,projection:we(e.projection)})||this).url_=e.url,r.imageExtent_=e.imageExtent,r.image_=new Ru(r.imageExtent_,void 0,1,r.url_,n,i),r.imageSize_=e.imageSize?e.imageSize:null,r.image_.addEventListener(N.CHANGE,r.handleImageChange.bind(r)),r}return yh(e,t),e.prototype.getImageExtent=function(){return this.imageExtent_},e.prototype.getImageInternal=function(t,e,r,n){return bt(t,this.image_.getExtent())?this.image_:null},e.prototype.getUrl=function(){return this.url_},e.prototype.handleImageChange=function(e){if(this.image_.getState()==Da.LOADED){var r=this.image_.getExtent(),n=this.image_.getImage(),i=void 0,o=void 0;this.imageSize_?(i=this.imageSize_[0],o=this.imageSize_[1]):(i=n.width,o=n.height);var a=Ot(r)/o,s=Math.ceil(It(r)/a);if(s!=i){var u=li(s,o),l=u.canvas;u.drawImage(n,0,0,i,o,0,0,l.width,l.height),this.image_.setImage(l)}}t.prototype.handleImageChange.call(this,e)},e}(sh),mh="1.3.0",Eh="carmentaserver",Th="geoserver",Sh="mapserver",wh="qgis",xh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Oh=[101,101],Rh=function(t){function e(e){var r=this,n=e||{};return(r=t.call(this,{attributions:n.attributions,projection:n.projection,resolutions:n.resolutions})||this).crossOrigin_=void 0!==n.crossOrigin?n.crossOrigin:null,r.url_=n.url,r.imageLoadFunction_=void 0!==n.imageLoadFunction?n.imageLoadFunction:ah,r.params_=n.params||{},r.v13_=!0,r.updateV13_(),r.serverType_=n.serverType,r.hidpi_=void 0===n.hidpi||n.hidpi,r.image_=null,r.imageSize_=[0,0],r.renderedRevision_=0,r.ratio_=void 0!==n.ratio?n.ratio:1.5,r}return xh(e,t),e.prototype.getFeatureInfoUrl=function(t,e,r,n){if(void 0!==this.url_){var i=we(r),o=this.getProjection();o&&o!==i&&(e=rl(o,i,t,e),t=Me(t,i,o));var a=xt(t,e,0,Oh),s={SERVICE:"WMS",VERSION:mh,REQUEST:"GetFeatureInfo",FORMAT:"image/png",TRANSPARENT:!0,QUERY_LAYERS:this.params_.LAYERS};p(s,this.params_,n);var u=Math.floor((t[0]-a[0])/e),l=Math.floor((a[3]-t[1])/e);return s[this.v13_?"I":"X"]=u,s[this.v13_?"J":"Y"]=l,this.getRequestUrl_(a,Oh,1,o||i,s)}},e.prototype.getLegendUrl=function(t,e){if(void 0!==this.url_){var r={SERVICE:"WMS",VERSION:mh,REQUEST:"GetLegendGraphic",FORMAT:"image/png"};if(void 0===e||void 0===e.LAYER){var n=this.params_.LAYERS;if(!(!Array.isArray(n)||1===n.length))return;r.LAYER=n}if(void 0!==t){var i=this.getProjection()?this.getProjection().getMetersPerUnit():1;r.SCALE=t*i*39.37*(25.4/.28)}return p(r,e),uh(this.url_,r)}},e.prototype.getParams=function(){return this.params_},e.prototype.getImageInternal=function(t,e,r,n){if(void 0===this.url_)return null;e=this.findNearestResolution(e),1==r||this.hidpi_&&void 0!==this.serverType_||(r=1);var i=e/r,o=St(t),a=xt(o,i,0,[Math.ceil(It(t)/i),Math.ceil(Ot(t)/i)]),s=xt(o,i,0,[Math.ceil(this.ratio_*It(t)/i),Math.ceil(this.ratio_*Ot(t)/i)]),u=this.image_;if(u&&this.renderedRevision_==this.getRevision()&&u.getResolution()==e&&u.getPixelRatio()==r&&it(u.getExtent(),a))return u;var l={SERVICE:"WMS",VERSION:mh,REQUEST:"GetMap",FORMAT:"image/png",TRANSPARENT:!0};p(l,this.params_),this.imageSize_[0]=Math.round(It(s)/i),this.imageSize_[1]=Math.round(Ot(s)/i);var h=this.getRequestUrl_(s,this.imageSize_,r,n,l);return this.image_=new Ru(s,e,r,h,this.crossOrigin_,this.imageLoadFunction_),this.renderedRevision_=this.getRevision(),this.image_.addEventListener(N.CHANGE,this.handleImageChange.bind(this)),this.image_},e.prototype.getImageLoadFunction=function(){return this.imageLoadFunction_},e.prototype.getRequestUrl_=function(t,e,r,n,i){if(K(void 0!==this.url_,9),i[this.v13_?"CRS":"SRS"]=n.getCode(),"STYLES"in this.params_||(i.STYLES=""),1!=r)switch(this.serverType_){case Th:var o=90*r+.5|0;"FORMAT_OPTIONS"in i?i.FORMAT_OPTIONS+=";dpi:"+o:i.FORMAT_OPTIONS="dpi:"+o;break;case Sh:i.MAP_RESOLUTION=90*r;break;case Eh:case wh:i.DPI=90*r;break;default:K(!1,8)}i.WIDTH=e[0],i.HEIGHT=e[1];var a,s=n.getAxisOrientation();return a=this.v13_&&"ne"==s.substr(0,2)?[t[1],t[0],t[3],t[2]]:t,i.BBOX=a.join(","),uh(this.url_,i)},e.prototype.getUrl=function(){return this.url_},e.prototype.setImageLoadFunction=function(t){this.image_=null,this.imageLoadFunction_=t,this.changed()},e.prototype.setUrl=function(t){t!=this.url_&&(this.url_=t,this.image_=null,this.changed())},e.prototype.updateParams=function(t){p(this.params_,t),this.updateV13_(),this.image_=null,this.changed()},e.prototype.updateV13_=function(){var t=this.params_.VERSION||mh;this.v13_=Yn(t,"1.3")>=0},e}(sh),Ch=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Ph='© OpenStreetMap contributors.',Ih=function(t){function e(e){var r,n=e||{};r=void 0!==n.attributions?n.attributions:[Ph];var i=void 0!==n.crossOrigin?n.crossOrigin:"anonymous",o=void 0!==n.url?n.url:"https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png";return t.call(this,{attributions:r,cacheSize:n.cacheSize,crossOrigin:i,opaque:void 0===n.opaque||n.opaque,maxZoom:void 0!==n.maxZoom?n.maxZoom:19,reprojectionErrorThreshold:n.reprojectionErrorThreshold,tileLoadFunction:n.tileLoadFunction,url:o,wrapX:n.wrapX,attributionsCollapsible:!1})||this}return Ch(e,t),e}(Cl),bh=r(3),Lh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Mh=function(t){function e(e){var r=e||{};return t.call(this,r)||this}return Lh(e,t),e}(Bi),Fh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Ah=function(t){function e(e){var r=t.call(this)||this;return r.boundHandleImageChange_=r.handleImageChange_.bind(r),r.layer_=e,r}return Fh(e,t),e.prototype.getFeatures=function(t){return n()},e.prototype.prepareFrame=function(t){return n()},e.prototype.renderFrame=function(t,e){return n()},e.prototype.loadedTileCallback=function(t,e,r){t[e]||(t[e]={}),t[e][r.tileCoord.toString()]=r},e.prototype.createLoadedTileFinder=function(t,e,r){return function(n,i){var o=this.loadedTileCallback.bind(this,r,n);return t.forEachLoadedTile(e,n,i,o)}.bind(this)},e.prototype.forEachFeatureAtCoordinate=function(t,e,r,n,i){},e.prototype.getDataAtPixel=function(t,e,r){return n()},e.prototype.getLayer=function(){return this.layer_},e.prototype.handleFontsChanged=function(){},e.prototype.handleImageChange_=function(t){t.target.getState()===Da.LOADED&&this.renderIfReadyAndVisible()},e.prototype.loadImage=function(t){var e=t.getState();return e!=Da.LOADED&&e!=Da.ERROR&&t.addEventListener(N.CHANGE,this.boundHandleImageChange_),e==Da.IDLE&&(t.load(),e=t.getState()),e==Da.LOADED},e.prototype.renderIfReadyAndVisible=function(){var t=this.getLayer();t.getVisible()&&t.getSourceState()==vi.READY&&t.changed()},e}(G),Nh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Dh=function(t){function e(e){var r=t.call(this,e)||this;return r.container=null,r.renderedResolution,r.tempTransform_=[1,0,0,1,0,0],r.pixelTransform=[1,0,0,1,0,0],r.inversePixelTransform=[1,0,0,1,0,0],r.context=null,r.containerReused=!1,r.createTransformStringCanvas_=li(1,1).canvas,r}return Nh(e,t),e.prototype.useContainer=function(t,e,r){var n,i,o=this.getLayer().getClassName();t&&""===t.style.opacity&&t.className===o&&((s=t.firstElementChild)instanceof HTMLCanvasElement&&(i=s.getContext("2d")));if(i&&i.canvas.style.transform===e?(this.container=t,this.context=i,this.containerReused=!0):this.containerReused&&(this.container=null,this.context=null,this.containerReused=!1),!this.container){(n=document.createElement("div")).className=o;var a=n.style;a.position="absolute",a.width="100%",a.height="100%";var s=(i=li()).canvas;n.appendChild(s),(a=s.style).position="absolute",a.left="0",a.transformOrigin="top left",this.container=n,this.context=i}},e.prototype.clip=function(t,e,r){var n=e.pixelRatio,i=e.size[0]*n/2,o=e.size[1]*n/2,a=e.viewState.rotation,s=Ct(r),u=Pt(r),l=Tt(r),h=Et(r);Ze(e.coordinateToPixelTransform,s),Ze(e.coordinateToPixelTransform,u),Ze(e.coordinateToPixelTransform,l),Ze(e.coordinateToPixelTransform,h),t.save(),La(t,-a,i,o),t.beginPath(),t.moveTo(s[0]*n,s[1]*n),t.lineTo(u[0]*n,u[1]*n),t.lineTo(l[0]*n,l[1]*n),t.lineTo(h[0]*n,h[1]*n),t.clip(),La(t,a,i,o)},e.prototype.clipUnrotated=function(t,e,r){var n=Ct(r),i=Pt(r),o=Tt(r),a=Et(r);Ze(e.coordinateToPixelTransform,n),Ze(e.coordinateToPixelTransform,i),Ze(e.coordinateToPixelTransform,o),Ze(e.coordinateToPixelTransform,a);var s=this.inversePixelTransform;Ze(s,n),Ze(s,i),Ze(s,o),Ze(s,a),t.save(),t.beginPath(),t.moveTo(Math.round(n[0]),Math.round(n[1])),t.lineTo(Math.round(i[0]),Math.round(i[1])),t.lineTo(Math.round(o[0]),Math.round(o[1])),t.lineTo(Math.round(a[0]),Math.round(a[1])),t.clip()},e.prototype.dispatchRenderEvent_=function(t,e,r){var n=this.getLayer();if(n.hasListener(t)){var i=new $o(t,this.inversePixelTransform,r,e);n.dispatchEvent(i)}},e.prototype.preRender=function(t,e){this.dispatchRenderEvent_(vn,t,e)},e.prototype.postRender=function(t,e){this.dispatchRenderEvent_(mn,t,e)},e.prototype.getRenderTransform=function(t,e,r,n,i,o,a){var s=i/2,u=o/2,l=n/e,h=-l,c=-t[0]+a,p=-t[1];return He(this.tempTransform_,s,u,l,h,-r,c,p)},e.prototype.getDataAtPixel=function(t,e,r){var n,i=Ze(this.inversePixelTransform,t.slice()),o=this.context;try{n=o.getImageData(Math.round(i[0]),Math.round(i[1]),1,1).data}catch(t){return"SecurityError"===t.name?new Uint8Array:n}return 0===n[3]?null:n},e.prototype.createTransformString=function(t){return this.createTransformStringCanvas_.style.transform=Je(t),this.createTransformStringCanvas_.style.transform},e}(Ah),Gh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),jh=function(t){function e(e){var r=t.call(this,e)||this;return r.image_=null,r}return Gh(e,t),e.prototype.getImage=function(){return this.image_?this.image_.getImage():null},e.prototype.prepareFrame=function(t){var e=t.layerStatesArray[t.layerIndex],r=t.pixelRatio,n=t.viewState,i=n.resolution,o=this.getLayer().getSource(),a=t.viewHints,s=t.extent;if(void 0!==e.extent&&(s=Rt(s,Ye(e.extent,n.projection))),!a[Dn]&&!a[Gn]&&!Lt(s)){var u=n.projection,l=o.getImage(s,i,r,u);l&&this.loadImage(l)&&(this.image_=l)}return!!this.image_},e.prototype.renderFrame=function(t,e){var r=this.image_,n=r.getExtent(),i=r.getResolution(),o=r.getPixelRatio(),a=t.layerStatesArray[t.layerIndex],s=t.pixelRatio,u=t.viewState,l=u.center,h=u.resolution,c=t.size,p=s*i/(h*o),f=Math.round(c[0]*s),d=Math.round(c[1]*s),_=u.rotation;if(_){var g=Math.round(Math.sqrt(f*f+d*d));f=g,d=g}He(this.pixelTransform,t.size[0]/2,t.size[1]/2,1/s,1/s,_,-f/2,-d/2),qe(this.inversePixelTransform,this.pixelTransform);var y=this.createTransformString(this.pixelTransform);this.useContainer(e,y,a.opacity);var v=this.context,m=v.canvas;m.width!=f||m.height!=d?(m.width=f,m.height=d):this.containerReused||v.clearRect(0,0,f,d);var E=!1;if(a.extent){var T=Ye(a.extent,u.projection);(E=!it(T,t.extent)&&bt(T,t.extent))&&this.clipUnrotated(v,t,T)}var S=r.getImage(),w=He(this.tempTransform_,f/2,d/2,p,p,0,o*(n[0]-l[0])/i,o*(l[1]-n[3])/i);this.renderedResolution=i*s/o;var x=w[4],O=w[5],R=S.width*w[0],C=S.height*w[3];if(this.preRender(v,t),R>=.5&&C>=.5){var P=a.opacity,I=void 0;1!==P&&(I=this.context.globalAlpha,this.context.globalAlpha=P),this.context.drawImage(S,0,0,+S.width,+S.height,Math.round(x),Math.round(O),Math.round(R),Math.round(C)),1!==P&&(this.context.globalAlpha=I)}return this.postRender(v,t),E&&v.restore(),y!==m.style.transform&&(m.style.transform=y),this.container},e}(Dh),kh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Uh=function(t){function e(e){return t.call(this,e)||this}return kh(e,t),e.prototype.createRenderer=function(){return new jh(this)},e}(Mh),Bh="preload",Yh="useInterimTilesOnError",zh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Xh=function(t){function e(e){var r=this,n=e||{},i=p({},n);return delete i.preload,delete i.useInterimTilesOnError,(r=t.call(this,i)||this).setPreload(void 0!==n.preload?n.preload:0),r.setUseInterimTilesOnError(void 0===n.useInterimTilesOnError||n.useInterimTilesOnError),r}return zh(e,t),e.prototype.getPreload=function(){return this.get(Bh)},e.prototype.setPreload=function(t){this.set(Bh,t)},e.prototype.getUseInterimTilesOnError=function(){return this.get(Yh)},e.prototype.setUseInterimTilesOnError=function(t){this.set(Yh,t)},e}(Bi),Vh=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Wh=function(t){function e(e){var r=t.call(this,e)||this;return r.extentChanged=!0,r.renderedExtent_=null,r.renderedPixelRatio,r.renderedProjection=null,r.renderedRevision,r.renderedTiles=[],r.newTiles_=!1,r.tmpExtent=[1/0,1/0,-1/0,-1/0],r.tmpTileRange_=new hs(0,0,0,0),r}return Vh(e,t),e.prototype.isDrawableTile=function(t){var e=this.getLayer(),r=t.getState(),n=e.getUseInterimTilesOnError();return r==wn.LOADED||r==wn.EMPTY||r==wn.ERROR&&!n},e.prototype.getTile=function(t,e,r,n){var i=n.pixelRatio,o=n.viewState.projection,a=this.getLayer(),s=a.getSource().getTile(t,e,r,i,o);return s.getState()==wn.ERROR&&(a.getUseInterimTilesOnError()?a.getPreload()>0&&(this.newTiles_=!0):s.setState(wn.LOADED)),this.isDrawableTile(s)||(s=s.getInterimTile()),s},e.prototype.loadedTileCallback=function(e,r,n){return!!this.isDrawableTile(n)&&t.prototype.loadedTileCallback.call(this,e,r,n)},e.prototype.prepareFrame=function(t){return!!this.getLayer().getSource()},e.prototype.renderFrame=function(t,e){var r=t.layerStatesArray[t.layerIndex],n=t.viewState,i=n.projection,a=n.resolution,s=n.center,u=n.rotation,l=t.pixelRatio,h=this.getLayer(),c=h.getSource(),p=c.getRevision(),f=c.getTileGridForProjection(i),d=f.getZForResolution(a,c.zDirection),_=f.getResolution(d),g=t.extent,y=r.extent&&Ye(r.extent,i);y&&(g=Rt(g,Ye(r.extent,i)));var v=c.getTilePixelRatio(l),m=Math.round(t.size[0]*v),T=Math.round(t.size[1]*v);if(u){var S=Math.round(Math.sqrt(m*m+T*T));m=S,T=S}var w=_*m/2/v,x=_*T/2/v,O=[s[0]-w,s[1]-x,s[0]+w,s[1]+x],R=f.getTileRangeForExtentAndZ(g,d),C={};C[d]={};var P=this.createLoadedTileFinder(c,i,C),I=this.tmpExtent,b=this.tmpTileRange_;this.newTiles_=!1;for(var L=R.minX;L<=R.maxX;++L)for(var M=R.minY;M<=R.maxY;++M){var F=this.getTile(d,L,M,t);if(this.isDrawableTile(F)){var A=o(this);if(F.getState()==wn.LOADED){C[d][F.tileCoord.toString()]=F;var N=F.inTransition(A);this.newTiles_||!N&&-1!==this.renderedTiles.indexOf(F)||(this.newTiles_=!0)}if(1===F.getAlpha(A,t.time))continue}var D=f.getTileCoordChildTileRange(F.tileCoord,b,I),G=!1;D&&(G=P(d+1,D)),G||f.forEachTileCoordParentTileRange(F.tileCoord,P,b,I)}var j=_/a;He(this.pixelTransform,t.size[0]/2,t.size[1]/2,1/v,1/v,u,-m/2,-T/2);var k=this.createTransformString(this.pixelTransform);this.useContainer(e,k,r.opacity);var U=this.context,B=U.canvas;qe(this.inversePixelTransform,this.pixelTransform),He(this.tempTransform_,m/2,T/2,j,j,0,-m/2,-T/2),B.width!=m||B.height!=T?(B.width=m,B.height=T):this.containerReused||U.clearRect(0,0,m,T),y&&this.clipUnrotated(U,t,y),this.preRender(U,t),this.renderedTiles.length=0;var Y,z,X,V=Object.keys(C).map(Number);V.sort(E),1!==r.opacity||this.containerReused&&!c.getOpaque(t.viewState.projection)?(Y=[],z=[]):V=V.reverse();for(var W=V.length-1;W>=0;--W){var Z=V[W],K=c.getTilePixelSize(Z,l,i),H=f.getResolution(Z)/_,q=K[0]*H*j,J=K[1]*H*j,Q=f.getTileCoordForCoordAndZ(Ct(O),Z),$=f.getTileCoordExtent(Q),tt=Ze(this.tempTransform_,[v*($[0]-O[0])/_,v*(O[3]-$[3])/_]),et=v*c.getGutterForProjection(i),rt=C[Z];for(var nt in rt){var it=(F=rt[nt]).tileCoord,ot=tt[0]-(Q[1]-it[1])*q,at=Math.round(ot+q),st=tt[1]-(Q[2]-it[2])*J,ut=Math.round(st+J),lt=at-(L=Math.round(ot)),ht=ut-(M=Math.round(st)),ct=d===Z;if(!(N=ct&&1!==F.getAlpha(o(this),t.time)))if(Y){U.save(),X=[L,M,L+lt,M,L+lt,M+ht,L,M+ht];for(var ft=0,dt=Y.length;ftStamen Design, under CC BY 3.0.',Ph],uc={terrain:{extension:"jpg",opaque:!0},"terrain-background":{extension:"jpg",opaque:!0},"terrain-labels":{extension:"png",opaque:!1},"terrain-lines":{extension:"png",opaque:!1},"toner-background":{extension:"png",opaque:!0},toner:{extension:"png",opaque:!0},"toner-hybrid":{extension:"png",opaque:!1},"toner-labels":{extension:"png",opaque:!1},"toner-lines":{extension:"png",opaque:!1},"toner-lite":{extension:"png",opaque:!0},watercolor:{extension:"jpg",opaque:!0}},lc={terrain:{minZoom:0,maxZoom:18},toner:{minZoom:0,maxZoom:20},watercolor:{minZoom:0,maxZoom:18}},hc=function(t){function e(e){var r=e.layer.indexOf("-"),n=-1==r?e.layer:e.layer.slice(0,r),i=lc[n],o=uc[e.layer],a=void 0!==e.url?e.url:"https://stamen-tiles-{a-d}.a.ssl.fastly.net/"+e.layer+"/{z}/{x}/{y}."+o.extension;return t.call(this,{attributions:sc,cacheSize:e.cacheSize,crossOrigin:"anonymous",maxZoom:null!=e.maxZoom?e.maxZoom:i.maxZoom,minZoom:null!=e.minZoom?e.minZoom:i.minZoom,opaque:o.opaque,reprojectionErrorThreshold:e.reprojectionErrorThreshold,tileLoadFunction:e.tileLoadFunction,transition:e.transition,url:a,wrapX:e.wrapX})||this}return ac(e,t),e}(Cl),cc=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function pc(t,e,r){var n=this.getTileGrid();if(n||(n=this.getTileGridForProjection(r)),!(n.getResolutions().length<=t[0])){1==e||this.hidpi_||(e=1);var i=n.getTileCoordExtent(t,this.tmpExtent_),o=xi(n.getTileSize(t[0]),this.tmpSize);1!=e&&(o=wi(o,e,this.tmpSize));var a={F:"image",FORMAT:"PNG32",TRANSPARENT:!0};return p(a,this.params_),this.getRequestUrl_(t,o,i,e,r,a)}}var fc=function(t){function e(e){var r=this,n=e||{};return(r=t.call(this,{attributions:n.attributions,cacheSize:n.cacheSize,crossOrigin:n.crossOrigin,projection:n.projection,reprojectionErrorThreshold:n.reprojectionErrorThreshold,tileGrid:n.tileGrid,tileLoadFunction:n.tileLoadFunction,tileUrlFunction:pc,url:n.url,urls:n.urls,wrapX:void 0===n.wrapX||n.wrapX,transition:n.transition})||this).params_=n.params||{},r.hidpi_=void 0===n.hidpi||n.hidpi,r.tmpExtent_=[1/0,1/0,-1/0,-1/0],r.setKey(r.getKeyForParams_()),r}return cc(e,t),e.prototype.getKeyForParams_=function(){var t=0,e=[];for(var r in this.params_)e[t++]=r+"-"+this.params_[r];return e.join("/")},e.prototype.getParams=function(){return this.params_},e.prototype.getRequestUrl_=function(t,e,r,n,i,o){var a=this.urls;if(a){var s,u=i.getCode().split(":").pop();if(o.SIZE=e[0]+","+e[1],o.BBOX=r.join(","),o.BBOXSR=u,o.IMAGESR=u,o.DPI=Math.round(o.DPI?o.DPI*n:90*n),1==a.length)s=a[0];else s=a[Vt(ds(t),a.length)];return uh(s.replace(/MapServer\/?$/,"MapServer/export").replace(/ImageServer\/?$/,"ImageServer/exportImage"),o)}},e.prototype.getTilePixelRatio=function(t){return this.hidpi_?t:1},e.prototype.updateParams=function(t){p(this.params_,t),this.setKey(this.getKeyForParams_())},e}(wl),dc=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),_c=function(t){function e(e,r,n){var i=t.call(this,e,wn.LOADED)||this;return i.tileSize_=r,i.text_=n,i.canvas_=null,i}return dc(e,t),e.prototype.getImage=function(){if(this.canvas_)return this.canvas_;var t=this.tileSize_,e=li(t[0],t[1]);return e.strokeStyle="grey",e.strokeRect(.5,.5,t[0]+.5,t[1]+.5),e.fillStyle="grey",e.strokeStyle="white",e.textAlign="center",e.textBaseline="middle",e.font="24px sans-serif",e.lineWidth=4,e.strokeText(this.text_,t[0]/2,t[1]/2,t[0]),e.fillText(this.text_,t[0]/2,t[1]/2,t[0]),this.canvas_=e.canvas,e.canvas},e.prototype.load=function(){},e}(qu),gc=function(t){function e(e){var r=e||{};return t.call(this,{opaque:!1,projection:r.projection,tileGrid:r.tileGrid,wrapX:void 0===r.wrapX||r.wrapX,zDirection:r.zDirection})||this}return dc(e,t),e.prototype.getTile=function(t,e,r){var n=ps(t,e,r);if(this.tileCache.containsKey(n))return this.tileCache.get(n);var i=xi(this.tileGrid.getTileSize(t)),o=[t,e,r],a=this.getTileCoordForTileUrlFunction(o),s=void 0;s=a?"z:"+a[0]+" x:"+a[1]+" y:"+a[2]:"none";var u=new _c(o,i,s);return this.tileCache.set(n,u),u},e}(Cl),yc=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),vc=function(t){function e(e){var r=t.call(this,{attributions:e.attributions,cacheSize:e.cacheSize,crossOrigin:e.crossOrigin,projection:we("EPSG:3857"),reprojectionErrorThreshold:e.reprojectionErrorThreshold,state:vi.LOADING,tileLoadFunction:e.tileLoadFunction,wrapX:void 0===e.wrapX||e.wrapX,transition:e.transition})||this;if(r.tileJSON_=null,r.tileSize_=e.tileSize,e.url)if(e.jsonp)Zu(e.url,r.handleTileJSONResponse.bind(r),r.handleTileJSONError.bind(r));else{var n=new XMLHttpRequest;n.addEventListener("load",r.onXHRLoad_.bind(r)),n.addEventListener("error",r.onXHRError_.bind(r)),n.open("GET",e.url),n.send()}else e.tileJSON?r.handleTileJSONResponse(e.tileJSON):K(!1,51);return r}return yc(e,t),e.prototype.onXHRLoad_=function(t){var e=t.target;if(!e.status||e.status>=200&&e.status<300){var r=void 0;try{r=JSON.parse(e.responseText)}catch(t){return void this.handleTileJSONError()}this.handleTileJSONResponse(r)}else this.handleTileJSONError()},e.prototype.onXHRError_=function(t){this.handleTileJSONError()},e.prototype.getTileJSON=function(){return this.tileJSON_},e.prototype.handleTileJSONResponse=function(t){var e,r=we("EPSG:4326"),n=this.getProjection();if(void 0!==t.bounds){var i=be(r,n);e=Ft(t.bounds,i)}var o=t.minzoom||0,a=t.maxzoom||22,s=vs({extent:Ts(n),maxZoom:a,minZoom:o,tileSize:this.tileSize_});if(this.tileGrid=s,this.tileUrlFunction=zu(t.tiles,s),void 0!==t.attribution&&!this.getAttributions()){var u=void 0!==e?e:r.getExtent();this.setAttributions(function(e){return bt(u,e.extent)?[t.attribution]:null})}this.tileJSON_=t,this.setState(vi.READY)},e.prototype.handleTileJSONError=function(){this.setState(vi.ERROR)},e}(wl),mc=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function Ec(t,e,r){var n=this.getTileGrid();if(n||(n=this.getTileGridForProjection(r)),!(n.getResolutions().length<=t[0])){1==e||this.hidpi_&&void 0!==this.serverType_||(e=1);var i=n.getResolution(t[0]),o=n.getTileCoordExtent(t,this.tmpExtent_),a=xi(n.getTileSize(t[0]),this.tmpSize),s=this.gutter_;0!==s&&(a=Si(a,s,this.tmpSize),o=tt(o,i*s,o)),1!=e&&(a=wi(a,e,this.tmpSize));var u={SERVICE:"WMS",VERSION:mh,REQUEST:"GetMap",FORMAT:"image/png",TRANSPARENT:!0};return p(u,this.params_),this.getRequestUrl_(t,a,o,e,r,u)}}var Tc=function(t){function e(e){var r=this,n=e||{},i=n.params||{},o=!("TRANSPARENT"in i)||i.TRANSPARENT;return(r=t.call(this,{attributions:n.attributions,cacheSize:n.cacheSize,crossOrigin:n.crossOrigin,opaque:!o,projection:n.projection,reprojectionErrorThreshold:n.reprojectionErrorThreshold,tileClass:n.tileClass,tileGrid:n.tileGrid,tileLoadFunction:n.tileLoadFunction,tileUrlFunction:Ec,url:n.url,urls:n.urls,wrapX:void 0===n.wrapX||n.wrapX,transition:n.transition})||this).gutter_=void 0!==n.gutter?n.gutter:0,r.params_=i,r.v13_=!0,r.serverType_=n.serverType,r.hidpi_=void 0===n.hidpi||n.hidpi,r.tmpExtent_=[1/0,1/0,-1/0,-1/0],r.updateV13_(),r.setKey(r.getKeyForParams_()),r}return mc(e,t),e.prototype.getFeatureInfoUrl=function(t,e,r,n){var i=we(r),o=this.getProjection(),a=this.getTileGrid();a||(a=this.getTileGridForProjection(i));var s=a.getZForResolution(e,this.zDirection),u=a.getTileCoordForCoordAndZ(t,s);if(!(a.getResolutions().length<=u[0])){var l=a.getResolution(u[0]),h=a.getTileCoordExtent(u,this.tmpExtent_),c=xi(a.getTileSize(u[0]),this.tmpSize),f=this.gutter_;0!==f&&(c=Si(c,f,this.tmpSize),h=tt(h,l*f,h)),o&&o!==i&&(l=rl(o,i,t,l),h=Fe(h,i,o),t=Me(t,i,o));var d={SERVICE:"WMS",VERSION:mh,REQUEST:"GetFeatureInfo",FORMAT:"image/png",TRANSPARENT:!0,QUERY_LAYERS:this.params_.LAYERS};p(d,this.params_,n);var _=Math.floor((t[0]-h[0])/l),g=Math.floor((h[3]-t[1])/l);return d[this.v13_?"I":"X"]=_,d[this.v13_?"J":"Y"]=g,this.getRequestUrl_(u,c,h,1,o||i,d)}},e.prototype.getLegendUrl=function(t,e){if(void 0!==this.urls[0]){var r={SERVICE:"WMS",VERSION:mh,REQUEST:"GetLegendGraphic",FORMAT:"image/png"};if(void 0===e||void 0===e.LAYER){var n=this.params_.LAYERS;if(!(!Array.isArray(n)||1===n.length))return;r.LAYER=n}if(void 0!==t){var i=this.getProjection()?this.getProjection().getMetersPerUnit():1;r.SCALE=t*i*39.37*(25.4/.28)}return p(r,e),uh(this.urls[0],r)}},e.prototype.getGutter=function(){return this.gutter_},e.prototype.getParams=function(){return this.params_},e.prototype.getRequestUrl_=function(t,e,r,n,i,o){var a=this.urls;if(a){if(o.WIDTH=e[0],o.HEIGHT=e[1],o[this.v13_?"CRS":"SRS"]=i.getCode(),"STYLES"in this.params_||(o.STYLES=""),1!=n)switch(this.serverType_){case Th:var s=90*n+.5|0;"FORMAT_OPTIONS"in o?o.FORMAT_OPTIONS+=";dpi:"+s:o.FORMAT_OPTIONS="dpi:"+s;break;case Sh:o.MAP_RESOLUTION=90*n;break;case Eh:case wh:o.DPI=90*n;break;default:K(!1,52)}var u,l=i.getAxisOrientation(),h=r;if(this.v13_&&"ne"==l.substr(0,2)){var c=void 0;c=r[0],h[0]=r[1],h[1]=c,c=r[2],h[2]=r[3],h[3]=c}if(o.BBOX=h.join(","),1==a.length)u=a[0];else u=a[Vt(ds(t),a.length)];return uh(u,o)}},e.prototype.getTilePixelRatio=function(t){return this.hidpi_&&void 0!==this.serverType_?t:1},e.prototype.getKeyForParams_=function(){var t=0,e=[];for(var r in this.params_)e[t++]=r+"-"+this.params_[r];return e.join("/")},e.prototype.updateParams=function(t){p(this.params_,t),this.updateV13_(),this.setKey(this.getKeyForParams_())},e.prototype.updateV13_=function(){var t=this.params_.VERSION||mh;this.v13_=Yn(t,"1.3")>=0},e}(wl),Sc=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),wc=function(t){function e(e,r,n,i,o,a){var s=t.call(this,e,r)||this;return s.src_=n,s.extent_=i,s.preemptive_=o,s.grid_=null,s.keys_=null,s.data_=null,s.jsonp_=a,s}return Sc(e,t),e.prototype.getImage=function(){return null},e.prototype.getData=function(t){if(!this.grid_||!this.keys_)return null;var e=(t[0]-this.extent_[0])/(this.extent_[2]-this.extent_[0]),r=(t[1]-this.extent_[1])/(this.extent_[3]-this.extent_[1]),n=this.grid_[Math.floor((1-r)*this.grid_.length)];if("string"!=typeof n)return null;var i=n.charCodeAt(Math.floor(e*n.length));i>=93&&i--,i>=35&&i--;var o=null;if((i-=32)in this.keys_){var a=this.keys_[i];o=this.data_&&a in this.data_?this.data_[a]:a}return o},e.prototype.forDataAtCoordinate=function(t,e,r){this.state==wn.IDLE&&!0===r?(y(this,N.CHANGE,function(r){e(this.getData(t))},this),this.loadInternal_()):!0===r?setTimeout(function(){e(this.getData(t))}.bind(this),0):e(this.getData(t))},e.prototype.getKey=function(){return this.src_},e.prototype.handleError_=function(){this.state=wn.ERROR,this.changed()},e.prototype.handleLoad_=function(t){this.grid_=t.grid,this.keys_=t.keys,this.data_=t.data,this.state=wn.EMPTY,this.changed()},e.prototype.loadInternal_=function(){if(this.state==wn.IDLE)if(this.state=wn.LOADING,this.jsonp_)Zu(this.src_,this.handleLoad_.bind(this),this.handleError_.bind(this));else{var t=new XMLHttpRequest;t.addEventListener("load",this.onXHRLoad_.bind(this)),t.addEventListener("error",this.onXHRError_.bind(this)),t.open("GET",this.src_),t.send()}},e.prototype.onXHRLoad_=function(t){var e=t.target;if(!e.status||e.status>=200&&e.status<300){var r=void 0;try{r=JSON.parse(e.responseText)}catch(t){return void this.handleError_()}this.handleLoad_(r)}else this.handleError_()},e.prototype.onXHRError_=function(t){this.handleError_()},e.prototype.load=function(){this.preemptive_&&this.loadInternal_()},e}(qu),xc=function(t){function e(e){var r=t.call(this,{projection:we("EPSG:3857"),state:vi.LOADING})||this;if(r.preemptive_=void 0===e.preemptive||e.preemptive,r.tileUrlFunction_=Vu,r.template_=void 0,r.jsonp_=e.jsonp||!1,e.url)if(r.jsonp_)Zu(e.url,r.handleTileJSONResponse.bind(r),r.handleTileJSONError.bind(r));else{var n=new XMLHttpRequest;n.addEventListener("load",r.onXHRLoad_.bind(r)),n.addEventListener("error",r.onXHRError_.bind(r)),n.open("GET",e.url),n.send()}else e.tileJSON?r.handleTileJSONResponse(e.tileJSON):K(!1,51);return r}return Sc(e,t),e.prototype.onXHRLoad_=function(t){var e=t.target;if(!e.status||e.status>=200&&e.status<300){var r=void 0;try{r=JSON.parse(e.responseText)}catch(t){return void this.handleTileJSONError()}this.handleTileJSONResponse(r)}else this.handleTileJSONError()},e.prototype.onXHRError_=function(t){this.handleTileJSONError()},e.prototype.getTemplate=function(){return this.template_},e.prototype.forDataAtCoordinateAndResolution=function(t,e,r,n){if(this.tileGrid){var i=this.tileGrid.getZForResolution(e,this.zDirection),o=this.tileGrid.getTileCoordForCoordAndZ(t,i);this.getTile(o[0],o[1],o[2],1,this.getProjection()).forDataAtCoordinate(t,r,n)}else!0===n?setTimeout(function(){r(null)},0):r(null)},e.prototype.handleTileJSONError=function(){this.setState(vi.ERROR)},e.prototype.handleTileJSONResponse=function(t){var e,r=we("EPSG:4326"),n=this.getProjection();if(void 0!==t.bounds){var i=be(r,n);e=Ft(t.bounds,i)}var o=t.minzoom||0,a=t.maxzoom||22,s=vs({extent:Ts(n),maxZoom:a,minZoom:o});this.tileGrid=s,this.template_=t.template;var u=t.grids;if(u){if(this.tileUrlFunction_=zu(u,s),void 0!==t.attribution){var l=void 0!==e?e:r.getExtent();this.setAttributions(function(e){return bt(l,e.extent)?[t.attribution]:null})}this.setState(vi.READY)}else this.setState(vi.ERROR)},e.prototype.getTile=function(t,e,r,n,i){var o=ps(t,e,r);if(this.tileCache.containsKey(o))return this.tileCache.get(o);var a=[t,e,r],s=this.getTileCoordForTileUrlFunction(a,i),u=this.tileUrlFunction_(s,n,i),l=new wc(a,void 0!==u?wn.IDLE:wn.EMPTY,void 0!==u?u:"",this.tileGrid.getTileCoordExtent(a),this.preemptive_,this.jsonp_);return this.tileCache.set(o,l),l},e.prototype.useTile=function(t,e,r){var n=ps(t,e,r);this.tileCache.containsKey(n)&&this.tileCache.get(n)},e}(_l),Oc=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Rc=function(t){function e(e,r,n,i,o,a){var s=t.call(this,e,r,{transition:0})||this;return s.context_={},s.executorGroups={},s.loadingSourceTiles=0,s.errorSourceTileKeys={},s.hitDetectionImageData=null,s.replayState_={},s.wantedResolution,s.getSourceTiles=o.bind(s,s),s.removeSourceTiles_=a,s.sourceTileGrid_=i,s.sourceTileListenerKeys=[],s.sourceZ=-1,s.hifi=!1,s.wrappedTileCoord=n,s}return Oc(e,t),e.prototype.disposeInternal=function(){for(var e in this.sourceTileListenerKeys.forEach(v),this.sourceTileListenerKeys.length=0,this.removeSourceTiles_(this),this.context_){var r=this.context_[e].canvas;r.width=0,r.height=0}for(var e in this.executorGroups)for(var n=this.executorGroups[e],i=0,o=n.length;i0&&_[0].tileCoord[0]===f)h=_,c=!0,p=f;else{h=[],p=f+1;do{--p,c=!0,u.forEachTileCoord(o,p,function(n){var i,o=fs(n);if(o in this.sourceTileByCoordKey_){var a=(i=this.sourceTileByCoordKey_[o]).getState();if(a===wn.LOADED||a===wn.ERROR||a===wn.EMPTY)return void h.push(i)}else if(p===f){var s=this.tileUrlFunction(n,t,e);void 0!==s&&((i=new this.tileClass(n,wn.IDLE,s,this.format_,this.tileLoadFunction)).extent=u.getTileCoordExtent(n),i.projection=e,i.resolution=u.getResolution(n[0]),this.sourceTileByCoordKey_[o]=i,i.addEventListener(N.CHANGE,this.handleTileChange.bind(this)),i.load())}if(c=!1,i&&i.getState()!==wn.EMPTY&&r.getState()===wn.IDLE){r.loadingSourceTiles++;var l=g(i,N.CHANGE,function(){var t,e,n,o=i.getState(),a=i.getKey();if(o===wn.LOADED||o===wn.ERROR){o===wn.LOADED?(t=r.sourceTileListenerKeys,e=l,n=t.indexOf(e),n>-1&&t.splice(n,1),v(l),r.loadingSourceTiles--,delete r.errorSourceTileKeys[a]):o===wn.ERROR&&(r.errorSourceTileKeys[a]=!0);var s=Object.keys(r.errorSourceTileKeys).length;r.loadingSourceTiles-s==0&&(r.hifi=0===s,r.sourceZ=f,r.setState(wn.LOADED))}});r.sourceTileListenerKeys.push(l)}}.bind(this)),c||(h.length=0)}while(!c&&p>d)}return r.getState()===wn.IDLE&&r.setState(wn.LOADING),c&&(r.hifi=f===p,r.sourceZ=p,r.getState()0&&(r.tileUrlFunction=Xu(o.map(Nc.bind(r)))),r}return Fc(e,t),e.prototype.setUrls=function(t){this.urls=t;var e=t.join("\n");this.setTileUrlFunction(Xu(t.map(Nc.bind(this))),e)},e.prototype.getDimensions=function(){return this.dimensions_},e.prototype.getFormat=function(){return this.format_},e.prototype.getLayer=function(){return this.layer_},e.prototype.getMatrixSet=function(){return this.matrixSet_},e.prototype.getRequestEncoding=function(){return this.requestEncoding_},e.prototype.getStyle=function(){return this.style_},e.prototype.getVersion=function(){return this.version_},e.prototype.getKeyForDimensions_=function(){var t=0,e=[];for(var r in this.dimensions_)e[t++]=r+"-"+this.dimensions_[r];return e.join("/")},e.prototype.updateDimensions=function(t){p(this.dimensions_,t),this.setKey(this.getKeyForDimensions_())},e}(wl);function Nc(t){var e=this.requestEncoding_,r={layer:this.layer_,style:this.style_,tilematrixset:this.matrixSet_};e==Mc.KVP&&p(r,{Service:"WMTS",Request:"GetTile",Version:this.version_,Format:this.format_}),t=e==Mc.KVP?uh(t,r):t.replace(/\{(\w+?)\}/g,function(t,e){return e.toLowerCase()in r?r[e.toLowerCase()]:t});var n=this.tileGrid,i=this.dimensions_;return function(r,o,a){if(r){var s={TileMatrix:n.getMatrixId(r[0]),TileCol:r[1],TileRow:r[2]};p(s,i);var u=t;return u=e==Mc.KVP?uh(u,s):u.replace(/\{(\w+?)\}/g,function(t,e){return s[e]})}}}var Dc=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Gc={GENERATE_BUFFERS:"GENERATE_BUFFERS"},jc=function(t){function e(e,r){var n=t.call(this,e)||this,i=r||{};return n.helper=new uu({postProcesses:i.postProcesses,uniforms:i.uniforms}),n}return Dc(e,t),e.prototype.disposeInternal=function(){this.helper.dispose(),t.prototype.disposeInternal.call(this)},e.prototype.getShaderCompileErrors=function(){return this.helper.getShaderCompileErrors()},e}(Ah);var kc=jc,Uc=new Blob(['var e="function"==typeof Object.assign?Object.assign:function(e,n){if(null==e)throw new TypeError("Cannot convert undefined or null to object");for(var t=Object(e),r=1,o=arguments.length;rthis.maxLineWidth&&(this.maxLineWidth=r.lineWidth,this.bufferedMaxExtent_=null)}else r.strokeStyle=void 0,r.lineCap=void 0,r.lineDash=null,r.lineDashOffset=void 0,r.lineJoin=void 0,r.lineWidth=void 0,r.miterLimit=void 0},e.prototype.createFill=function(t){var e=t.fillStyle,r=[Qc.SET_FILL_STYLE,e];return"string"!=typeof e&&r.push(!0),r},e.prototype.applyStroke=function(t){this.instructions.push(this.createStroke(t))},e.prototype.createStroke=function(t){return[Qc.SET_STROKE_STYLE,t.strokeStyle,t.lineWidth*this.pixelRatio,t.lineCap,t.lineJoin,t.miterLimit,this.applyPixelRatio(t.lineDash),t.lineDashOffset*this.pixelRatio]},e.prototype.updateFillStyle=function(t,e){var r=t.fillStyle;"string"==typeof r&&t.currentFillStyle==r||(void 0!==r&&this.instructions.push(e.call(this,t)),t.currentFillStyle=r)},e.prototype.updateStrokeStyle=function(t,e){var r=t.strokeStyle,n=t.lineCap,i=t.lineDash,o=t.lineDashOffset,a=t.lineJoin,s=t.lineWidth,u=t.miterLimit;(t.currentStrokeStyle!=r||t.currentLineCap!=n||i!=t.currentLineDash&&!R(t.currentLineDash,i)||t.currentLineDashOffset!=o||t.currentLineJoin!=a||t.currentLineWidth!=s||t.currentMiterLimit!=u)&&(void 0!==r&&e.call(this,t),t.currentStrokeStyle=r,t.currentLineCap=n,t.currentLineDash=i,t.currentLineDashOffset=o,t.currentLineJoin=a,t.currentLineWidth=s,t.currentMiterLimit=u)},e.prototype.endGeometry=function(t){this.beginGeometryInstruction1_[2]=this.instructions.length,this.beginGeometryInstruction1_=null,this.beginGeometryInstruction2_[2]=this.hitDetectionInstructions.length,this.beginGeometryInstruction2_=null;var e=[Qc.END_GEOMETRY,t];this.instructions.push(e),this.hitDetectionInstructions.push(e)},e.prototype.getBufferedMaxExtent=function(){if(!this.bufferedMaxExtent_&&(this.bufferedMaxExtent_=et(this.maxExtent),this.maxLineWidth>0)){var t=this.resolution*(this.maxLineWidth+1)/2;tt(this.bufferedMaxExtent_,t,this.bufferedMaxExtent_)}return this.bufferedMaxExtent_},e}(fa),ep=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),rp=function(t){function e(e,r,n,i){var o=t.call(this,e,r,n,i)||this;return o.declutterGroups_=null,o.hitDetectionImage_=null,o.image_=null,o.anchorX_=void 0,o.anchorY_=void 0,o.height_=void 0,o.opacity_=void 0,o.originX_=void 0,o.originY_=void 0,o.rotateWithView_=void 0,o.rotation_=void 0,o.scale_=void 0,o.width_=void 0,o}return ep(e,t),e.prototype.drawCoordinates_=function(t,e,r,n){return this.appendFlatCoordinates(t,e,r,n,!1,!1)},e.prototype.drawPoint=function(t,e){if(this.image_){this.beginGeometry(t,e);var r=t.getFlatCoordinates(),n=t.getStride(),i=this.coordinates.length,o=this.drawCoordinates_(r,0,r.length,n);this.instructions.push([Qc.DRAW_IMAGE,i,o,this.image_,this.anchorX_,this.anchorY_,this.declutterGroups_,this.height_,this.opacity_,this.originX_,this.originY_,this.rotateWithView_,this.rotation_,this.scale_*this.pixelRatio,this.width_]),this.hitDetectionInstructions.push([Qc.DRAW_IMAGE,i,o,this.hitDetectionImage_,this.anchorX_,this.anchorY_,this.declutterGroups_,this.height_,this.opacity_,this.originX_,this.originY_,this.rotateWithView_,this.rotation_,this.scale_,this.width_]),this.endGeometry(e)}},e.prototype.drawMultiPoint=function(t,e){if(this.image_){this.beginGeometry(t,e);var r=t.getFlatCoordinates(),n=t.getStride(),i=this.coordinates.length,o=this.drawCoordinates_(r,0,r.length,n);this.instructions.push([Qc.DRAW_IMAGE,i,o,this.image_,this.anchorX_,this.anchorY_,this.declutterGroups_,this.height_,this.opacity_,this.originX_,this.originY_,this.rotateWithView_,this.rotation_,this.scale_*this.pixelRatio,this.width_]),this.hitDetectionInstructions.push([Qc.DRAW_IMAGE,i,o,this.hitDetectionImage_,this.anchorX_,this.anchorY_,this.declutterGroups_,this.height_,this.opacity_,this.originX_,this.originY_,this.rotateWithView_,this.rotation_,this.scale_,this.width_]),this.endGeometry(e)}},e.prototype.finish=function(){return this.reverseHitDetectionInstructions(),this.anchorX_=void 0,this.anchorY_=void 0,this.hitDetectionImage_=null,this.image_=null,this.height_=void 0,this.scale_=void 0,this.opacity_=void 0,this.originX_=void 0,this.originY_=void 0,this.rotateWithView_=void 0,this.rotation_=void 0,this.width_=void 0,t.prototype.finish.call(this)},e.prototype.setImageStyle=function(t,e){var r=t.getAnchor(),n=t.getSize(),i=t.getHitDetectionImage(1),o=t.getImage(1),a=t.getOrigin();this.anchorX_=r[0],this.anchorY_=r[1],this.declutterGroups_=e,this.hitDetectionImage_=i,this.image_=o,this.height_=n[1],this.opacity_=t.getOpacity(),this.originX_=a[0],this.originY_=a[1],this.rotateWithView_=t.getRotateWithView(),this.rotation_=t.getRotation(),this.scale_=t.getScale(),this.width_=n[0]},e}(tp),np=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),ip=function(t){function e(e,r,n,i){return t.call(this,e,r,n,i)||this}return np(e,t),e.prototype.drawFlatCoordinates_=function(t,e,r,n){var i=this.coordinates.length,o=this.appendFlatCoordinates(t,e,r,n,!1,!1),a=[Qc.MOVE_TO_LINE_TO,i,o];return this.instructions.push(a),this.hitDetectionInstructions.push(a),r},e.prototype.drawLineString=function(t,e){var r=this.state,n=r.strokeStyle,i=r.lineWidth;if(void 0!==n&&void 0!==i){this.updateStrokeStyle(r,this.applyStroke),this.beginGeometry(t,e),this.hitDetectionInstructions.push([Qc.SET_STROKE_STYLE,r.strokeStyle,r.lineWidth,r.lineCap,r.lineJoin,r.miterLimit,r.lineDash,r.lineDashOffset],qc);var o=t.getFlatCoordinates(),a=t.getStride();this.drawFlatCoordinates_(o,0,o.length,a),this.hitDetectionInstructions.push(Hc),this.endGeometry(e)}},e.prototype.drawMultiLineString=function(t,e){var r=this.state,n=r.strokeStyle,i=r.lineWidth;if(void 0!==n&&void 0!==i){this.updateStrokeStyle(r,this.applyStroke),this.beginGeometry(t,e),this.hitDetectionInstructions.push([Qc.SET_STROKE_STYLE,r.strokeStyle,r.lineWidth,r.lineCap,r.lineJoin,r.miterLimit,r.lineDash,r.lineDashOffset],qc);for(var o=t.getEnds(),a=t.getFlatCoordinates(),s=t.getStride(),u=0,l=0,h=o.length;lt&&(y>g&&(g=y,d=v,_=o),y=0,v=o-i)),a=s,h=p,c=f),u=m,l=E}return(y+=s)>g?[v,o]:[d,_]}var up=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),lp={left:0,end:0,center:.5,right:1,start:1,top:0,middle:.5,hanging:.2,alphabetic:.8,ideographic:.8,bottom:1},hp={Circle:ap,Default:tp,Image:rp,LineString:ip,Polygon:ap,Text:function(t){function e(e,r,n,i){var o=t.call(this,e,r,n,i)||this;return o.declutterGroups_,o.labels_=null,o.text_="",o.textOffsetX_=0,o.textOffsetY_=0,o.textRotateWithView_=void 0,o.textRotation_=0,o.textFillState_=null,o.fillStates={},o.textStrokeState_=null,o.strokeStates={},o.textState_={},o.textStates={},o.textKey_="",o.fillKey_="",o.strokeKey_="",Ea.prune(),o}return up(e,t),e.prototype.finish=function(){var e=t.prototype.finish.call(this);return e.textStates=this.textStates,e.fillStates=this.fillStates,e.strokeStates=this.strokeStates,e},e.prototype.drawText=function(t,e){var r=this.textFillState_,n=this.textStrokeState_,i=this.textState_;if(""!==this.text_&&i&&(r||n)){var o,a,s=this.coordinates.length,u=t.getType(),l=null,h=2,c=2;if(i.placement===ku.LINE){if(!bt(this.getBufferedMaxExtent(),t.getExtent()))return;var p=void 0;if(l=t.getFlatCoordinates(),c=t.getStride(),u==Nt.LINE_STRING)p=[l.length];else if(u==Nt.MULTI_LINE_STRING)p=t.getEnds();else if(u==Nt.POLYGON)p=t.getEnds().slice(0,1);else if(u==Nt.MULTI_POLYGON){var f=t.getEndss();for(p=[],o=0,a=f.length;ot[r-n],_=i.length,g=t[e],y=t[e+1],v=t[e+=n],m=t[e+1],E=0,T=Math.sqrt(Math.pow(v-g,2)+Math.pow(m-y,2)),S=!1,w=0;w<_;++w){for(var x=i[c=d?_-w-1:w],O=s*u(l,x,h),R=o+O/2;e0?-Math.PI:Math.PI),void 0!==p){var I=P-p;if(S=S||0!==I,I+=I>Math.PI?-2*Math.PI:I<-Math.PI?2*Math.PI:0,Math.abs(I)>a)return null}p=P;var b=C/T,L=Wt(g,v,b),M=Wt(y,m,b);f[c]=[L,M,O/2,P,x],o+=O}return S?f:[[f[0][0],f[0][1],f[0][2],f[0][3],i]]}var dp=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),_p=[1/0,1/0,-1/0,-1/0],gp=[1,0,0,1,0,0],yp=[],vp=[],mp=[],Ep=[],Tp=function(t){function e(e,r,n,i){var o=t.call(this)||this;return o.overlaps=n,o.pixelRatio=r,o.resolution=e,o.alignFill_,o.declutterItems=[],o.instructions=i.instructions,o.coordinates=i.coordinates,o.coordinateCache_={},o.renderedTransform_=[1,0,0,1,0,0],o.hitDetectionInstructions=i.hitDetectionInstructions,o.pixelCoordinates_=null,o.viewRotation_=0,o.fillStates=i.fillStates||{},o.strokeStates=i.strokeStates||{},o.textStates=i.textStates||{},o.widths_={},o}return dp(e,t),e.prototype.disposeInternal=function(){Ea.release(this),t.prototype.disposeInternal.call(this)},e.prototype.getTextImage=function(t,e,r,n){var i,o=n+e+t+r+this.pixelRatio;if(!Ea.containsKey(o)){var a=n?this.strokeStates[n]:null,s=r?this.fillStates[r]:null,u=this.textStates[e],l=this.pixelRatio,h=u.scale*l,c=lp[u.textAlign||"center"],p=n&&a.lineWidth?a.lineWidth:0,f=t.split("\n"),d=f.length,_=[],g=function(t,e,r){for(var n=e.length,i=0,o=0;on.width?n.width-l:d,E=s+h>n.height?n.height-h:s,T=_[3]+m*p+_[1],S=_[0]+E*p+_[2],w=e-_[3],x=r-_[0];(v||0!==c)&&(yp[0]=w,Ep[0]=w,yp[1]=x,vp[1]=x,vp[0]=w+T,mp[0]=vp[0],mp[1]=x+S,Ep[1]=mp[1]);var O=null;if(0!==c){var R=e+i,C=r+o;O=He(gp,R,C,1,1,c,-R,-C),Ze(gp,yp),Ze(gp,vp),Ze(gp,mp),Ze(gp,Ep),ut(Math.min(yp[0],vp[0],mp[0],Ep[0]),Math.min(yp[1],vp[1],mp[1],Ep[1]),Math.max(yp[0],vp[0],mp[0],Ep[0]),Math.max(yp[1],vp[1],mp[1],Ep[1]),_p)}else ut(w,x,w+T,x+S,_p);var P=t.canvas,I=y?y[2]*p/2:0,b=_p[0]-I<=P.width&&_p[2]+I>=0&&_p[1]-I<=P.height&&_p[3]+I>=0;if(f&&(e=Math.round(e),r=Math.round(r)),a){if(!b&&1==a[4])return;ft(a,_p);var L=b?[t,O?O.slice(0):null,u,n,l,h,m,E,e,r,p]:null;L&&(v&&L.push(g,y,yp,vp,mp,Ep),a.push(L))}else b&&(v&&this.replayTextBackground_(t,yp,vp,mp,Ep,g,y),Fa(t,O,u,n,l,h,m,E,e,r,p))},e.prototype.fill_=function(t){if(this.alignFill_){var e=Ze(this.renderedTransform_,[0,0]),r=512*this.pixelRatio;t.save(),t.translate(e[0]%r,e[1]%r),t.rotate(this.viewRotation_)}t.fill(),this.alignFill_&&t.restore()},e.prototype.setStrokeStyle_=function(t,e){t.strokeStyle=e[1],t.lineWidth=e[2],t.lineCap=e[3],t.lineJoin=e[4],t.miterLimit=e[5],t.setLineDash&&(t.lineDashOffset=e[7],t.setLineDash(e[6]))},e.prototype.renderDeclutter=function(t,e,r,n){if(t&&t.length>5){var i=t[4];if(1==i||i==t.length-5){var o={minX:t[0],minY:t[1],maxX:t[2],maxY:t[3],value:e};if(n||(n=new Ml.a(9)),!n.collides(o)){n.insert(o);for(var a=5,s=t.length;a11&&this.replayTextBackground_(u[0],u[13],u[14],u[15],u[16],u[11],u[12]),Fa.apply(void 0,u),h!==r&&(l.globalAlpha=h)}}t.length=5,lt(t)}}return n},e.prototype.drawTextImageWithPointPlacement_=function(t,e,r,n){var i=this.textStates[e],o=this.getTextImage(t,e,n,r),a=this.strokeStates[r],s=this.pixelRatio,u=lp[i.textAlign||"center"],l=lp[i.textBaseline||"middle"],h=a&&a.lineWidth?a.lineWidth:0;return{label:o,anchorX:u*(o.width/s-2*i.scale)+2*(.5-u)*h,anchorY:l*o.height/s+2*(.5-l)*h}},e.prototype.execute_=function(t,e,r,n,i,o){var a,s,u;this.declutterItems.length=0,this.pixelCoordinates_&&R(e,this.renderedTransform_)?a=this.pixelCoordinates_:(this.pixelCoordinates_||(this.pixelCoordinates_=[]),a=Dt(this.coordinates,0,this.coordinates.length,2,e,this.pixelCoordinates_),s=this.renderedTransform_,u=e,s[0]=u[0],s[1]=u[1],s[2]=u[2],s[3]=u[3],s[4]=u[4],s[5]=u[5]);for(var l,h,c,p,f,d,_,g,y,v,m,E,T,S,w,x,O,C=0,P=r.length,I=0,b=0,L=0,M=null,F=null,A=this.coordinateCache_,N=this.viewRotation_,D=Math.round(1e12*Math.atan2(-e[1],e[0]))/1e12,G={context:t,pixelRatio:this.pixelRatio,resolution:this.resolution,rotation:N},j=this.instructions!=r||this.overlaps?0:200;Cj&&(this.fill_(t),b=0),L>j&&(t.stroke(),L=0),b||L||(t.beginPath(),p=NaN,f=NaN),++C;break;case Qc.CIRCLE:var U=a[I=k[1]],B=a[I+1],Y=a[I+2]-U,z=a[I+3]-B,X=Math.sqrt(Y*Y+z*z);t.moveTo(U+X,B),t.arc(U,B,X,0,2*Math.PI,!0),++C;break;case Qc.CLOSE_PATH:t.closePath(),++C;break;case Qc.CUSTOM:I=k[1],l=k[2];var V=k[3],W=k[4],Z=6==k.length?k[5]:void 0;G.geometry=V,G.feature=w,C in A||(A[C]=[]);var K=A[C];Z?Z(a,I,l,2,K):(K[0]=a[I],K[1]=a[I+1],K.length=2),W(K,G),++C;break;case Qc.DRAW_IMAGE:I=k[1],l=k[2],v=k[3],h=k[4],c=k[5],y=i?null:k[6];var H=k[7],q=k[8],J=k[9],Q=k[10],$=k[11],tt=k[12],et=k[13],rt=k[14];if(!v&&k.length>=19){m=k[18],E=k[19],T=k[20],S=k[21];var nt=this.drawTextImageWithPointPlacement_(m,E,T,S);v=nt.label,k[3]=v;var it=k[22];h=(nt.anchorX-it)*this.pixelRatio,k[4]=h;var ot=k[23];c=(nt.anchorY-ot)*this.pixelRatio,k[5]=c,H=v.height,k[7]=H,rt=v.width,k[14]=rt}var at=void 0;k.length>24&&(at=k[24]);var st=void 0,ut=void 0,lt=void 0;k.length>16?(st=k[15],ut=k[16],lt=k[17]):(st=ma,ut=!1,lt=!1),$&&D?tt+=N:$||D||(tt-=N);for(var ht=0,ct=0;I=o;)Rp(r,t+i,t+o),Rp(r,t+o,t+i),Rp(r,t-o,t+i),Rp(r,t-i,t+o),Rp(r,t-i,t-o),Rp(r,t-o,t-i),Rp(r,t+o,t-i),Rp(r,t+i,t-o),2*((a+=1+2*++o)-i)+1>0&&(a+=1-2*(i-=1));return Op[t]=r,r}(n);function p(t){for(var e=l.getImageData(0,0,a,a).data,r=0;r0){var s=void 0;return(!o||h!=Ga.IMAGE&&h!=Ga.TEXT||-1!==o.indexOf(t))&&(s=i(t)),s||void l.clearRect(0,0,a,a)}}var f,d,_,g,y,v=Object.keys(this.executorsByZIndex_).map(Number);for(v.sort(E),f=v.length-1;f>=0;--f){var m=v[f].toString();for(_=this.executorsByZIndex_[m],d=wp.length-1;d>=0;--d)if(void 0!==(g=_[h=wp[d]])&&(y=g.executeHitDetection(l,s,r,p,u)))return y}},e.prototype.getClipCoords=function(t){var e=this.maxExtent_;if(!e)return null;var r=e[0],n=e[1],i=e[2],o=e[3],a=[r,n,r,o,i,o,i,n];return Dt(a,0,8,2,t,a),a},e.prototype.isEmpty=function(){return _(this.executorsByZIndex_)},e.prototype.execute=function(t,e,r,n,i,o){var a=Object.keys(this.executorsByZIndex_).map(Number);a.sort(E),this.maxExtent_&&(t.save(),this.clip(t,e));var s,u,l,h,c,p,f=i||wp;for(s=0,u=a.length;s=i)for(n=i;n0&&(a.width=0),this.container;var u=Math.round(t.size[0]*r),l=Math.round(t.size[1]*r);a.width!=u||a.height!=l?(a.width=u,a.height=l,a.style.transform!==i&&(a.style.transform=i)):this.containerReused||o.clearRect(0,0,u,l),this.preRender(o,t);var h=t.extent,c=t.viewState,p=c.center,f=c.resolution,d=c.projection,_=c.rotation,g=d.getExtent(),y=this.getLayer().getSource(),v=!1;if(n.extent){var m=Ye(n.extent,d);(v=!it(m,t.extent)&&bt(m,t.extent))&&this.clip(o,t,m)}var E=t.viewHints,T=!(E[Dn]||E[Gn]),S=this.getRenderTransform(p,f,_,r,u,l,0),w=this.getLayer().getDeclutter()?{}:null;if(s.execute(o,S,_,T,void 0,w),y.getWrapX()&&d.canWrapX()&&!it(g,h)){for(var x=h[0],O=It(g),R=0,C=void 0;xg[2];){C=O*++R;var I=this.getRenderTransform(p,f,_,r,u,l,C);s.execute(o,I,_,T,void 0,w),x-=O}}if(w){var b=t.viewHints;Cp(w,o,_,1,!(b[Dn]||b[Gn]),t.declutterItems)}v&&o.restore(),this.postRender(o,t);var L=n.opacity,M=this.container;return L!==parseFloat(M.style.opacity)&&(M.style.opacity=1===L?"":L),this.container},e.prototype.getFeatures=function(t){return new Promise(function(e,r){this.hitDetectionImageData_||this.animatingOrInteracting_?e(bp(t,this.renderedFeatures_,this.hitDetectionImageData_)):requestAnimationFrame(function(){var r=[this.context.canvas.width,this.context.canvas.height];Ze(this.pixelTransform,r);var n=this.renderedCenter_,i=this.renderedResolution_,o=this.renderedRotation_,a=this.renderedProjection_,s=this.renderedExtent_,u=this.getLayer(),l=[],h=r[0]/2,c=r[1]/2;l.push(this.getRenderTransform(n,i,o,.5,h,c,0).slice());var p=u.getSource(),f=a.getExtent();if(p.getWrapX()&&a.canWrapX()&&!it(f,s)){for(var d=s[0],_=It(f),g=0,y=void 0;df[2];)y=_*++g,l.push(this.getRenderTransform(n,i,o,.5,h,c,y).slice()),d-=_}this.hitDetectionImageData_=Ip(r,l,this.renderedFeatures_,u.getStyleFunction(),s,i,o),e(bp(t,this.renderedFeatures_,this.hitDetectionImageData_))}.bind(this))}.bind(this))},e.prototype.forEachFeatureAtCoordinate=function(t,e,r,n,i){if(this.replayGroup_){var a=e.viewState.resolution,s=e.viewState.rotation,u=this.getLayer(),l={};return this.replayGroup_.forEachFeatureAtCoordinate(t,a,s,r,function(t){var e=o(t);if(!(e in l))return l[e]=!0,n(t,u)},u.getDeclutter()?i:null)}},e.prototype.handleFontsChanged=function(){var t=this.getLayer();t.getVisible()&&this.replayGroup_&&t.changed()},e.prototype.handleStyleImageChange_=function(t){this.renderIfReadyAndVisible()},e.prototype.prepareFrame=function(t){var e=this.getLayer(),r=e.getSource(),n=t.viewHints[Dn],i=t.viewHints[Gn],o=e.getUpdateWhileAnimating(),a=e.getUpdateWhileInteracting();if(!this.dirty_&&!o&&n||!a&&i)return this.animatingOrInteracting_=!0,!0;this.animatingOrInteracting_=!1;var s=t.extent,u=t.viewState,l=u.projection,h=u.resolution,c=t.pixelRatio,p=e.getRevision(),f=e.getRenderBuffer(),d=e.getRenderOrder();void 0===d&&(d=Ua);var _=u.center.slice(),g=tt(s,f*h),y=u.projection.getExtent();if(r.getWrapX()&&u.projection.canWrapX()&&!it(y,t.extent)){var v=It(y),m=Math.max(It(g)/2,v);g[0]=y[0]-m,g[2]=y[2]+m;var E=Math.floor((_[0]-y[0])/v);_[0]-=E*v}if(!this.dirty_&&this.renderedResolution_==h&&this.renderedRevision_==p&&this.renderedRenderOrder_==d&&it(this.renderedExtent_,g))return this.replayGroupChanged=!1,!0;this.replayGroup_&&this.replayGroup_.dispose(),this.replayGroup_=null,this.dirty_=!1;var T,S=new cp(Ya(h,c),g,h,c,e.getDeclutter()),w=je();w?(r.loadFeatures(Be(g,l),h,w),T=be(w,l)):r.loadFeatures(g,h,l);var x=Ba(h,c),O=function(t){var r,n=t.getStyleFunction()||e.getStyleFunction();if(n&&(r=n(t,h)),r){var i=this.renderFeature(t,x,r,S,T);this.dirty_=this.dirty_||i}}.bind(this),R=Be(g,l),C=r.getFeaturesInExtent(R);d&&C.sort(d);for(var P=0,I=C.length;P=0;--R){var C=S[R];if(C.getState()!=wn.ABORT)for(var P=C.tileCoord,I=w.getTileCoordExtent(C.wrappedTileCoord),b=w.getTileCoordExtent(P,this.tmpExtent)[0]-I[0],L=Ve(Ke(this.inversePixelTransform.slice(),1/d,1/d),this.getRenderTransform(g,y,v,d,E,T,b)),M=C.executorGroups[o(a)],F=!1,A=0,N=M.length;A8){e.animate=!0;break}var n=this.renderTileImageQueue_[r];delete this.renderTileImageQueue_[r],this.renderTileImage_(n,e)}},e.prototype.renderFeature=function(t,e,r,n){if(!r)return!1;var i=!1;if(Array.isArray(r))for(var o=0,a=r.length;o>1)],e))<0?a=n+1:(s=n,u=!i);return u?a:~a}(p,g);if(y<0){var v=(g-p[-y-2])/(p[-y-1]-p[-y-2]),m=e+(-y-2)*n;a=Wt(t[m],t[m+n],v),s=Wt(t[m+1],t[m+n+1],v)}else a=t[e+y*n],s=t[e+y*n+1]}return o?(o[0]=a,o[1]=s,o):[a,s]}function zp(t,e,r,n,i,o){if(r==e)return null;var a;if(i>1;i0&&g.length>0;)o=g.pop(),h=d.pop(),p=_.pop(),(u=o.toString())in y||(l.push(p[0],p[1]),y[u]=!0),a=g.pop(),c=d.pop(),f=_.pop(),Bt((i=e(n=t(s=(o+a)/2)))[0],i[1],p[0],p[1],f[0],f[1])this.featurePool_.length;)s=new q,this.featurePool_.push(s);var l=n.getFeaturesCollection();l.clear();var h,c,p,f=0;for(h=0,c=this.meridians_.length;h0)break}this.source_&&(this.source_.clear(),this.source_.addFeatures(a)),this.dispatchEvent(new uf(sf,t,a,i))},e.prototype.registerListeners_=function(){var t=this.getMap();if(t){var e=this.target?this.target:t.getViewport();this.dropListenKeys_=[g(e,N.DROP,lf,this),g(e,N.DRAGENTER,hf,this),g(e,N.DRAGOVER,hf,this),g(e,N.DROP,hf,this)]}},e.prototype.setActive=function(e){!this.getActive()&&e&&this.registerListeners_(),this.getActive()&&!e&&this.unregisterListeners_(),t.prototype.setActive.call(this,e)},e.prototype.setMap=function(e){this.unregisterListeners_(),t.prototype.setMap.call(this,e),this.getActive()&&this.registerListeners_()},e.prototype.tryReadFeatures_=function(t,e,r){try{return t.readFeatures(e,r)}catch(t){return null}},e.prototype.unregisterListeners_=function(){this.dropListenKeys_&&(this.dropListenKeys_.forEach(v),this.dropListenKeys_=null)},e}(to),pf=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),ff=function(t){function e(e){var r=this,n=e||{};return(r=t.call(this,n)||this).condition_=n.condition?n.condition:po,r.lastAngle_=void 0,r.lastMagnitude_=void 0,r.lastScaleDelta_=0,r.duration_=void 0!==n.duration?n.duration:400,r}return pf(e,t),e.prototype.handleDragEvent=function(t){if(_o(t)){var e=t.map,r=e.getSize(),n=t.pixel,i=n[0]-r[0]/2,o=r[1]/2-n[1],a=Math.atan2(o,i),s=Math.sqrt(i*i+o*o),u=e.getView();if(void 0!==this.lastAngle_){var l=this.lastAngle_-a;u.adjustRotationInternal(l)}this.lastAngle_=a,void 0!==this.lastMagnitude_&&u.adjustResolutionInternal(this.lastMagnitude_/s),void 0!==this.lastMagnitude_&&(this.lastScaleDelta_=this.lastMagnitude_/s),this.lastMagnitude_=s}},e.prototype.handleUpEvent=function(t){if(!_o(t))return!0;var e=t.map.getView(),r=this.lastScaleDelta_>1?1:-1;return e.endInteraction(this.duration_,r),this.lastScaleDelta_=0,!1},e.prototype.handleDownEvent=function(t){return!!_o(t)&&(!!this.condition_(t)&&(t.map.getView().beginInteraction(),this.lastAngle_=void 0,this.lastMagnitude_=void 0,!0))},e}(mo),df=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),_f=function(t){function e(e,r,n){var i=t.call(this)||this;if(void 0!==n&&void 0===r)i.setFlatCoordinates(n,e);else{var o=r||0;i.setCenterAndRadius(e,o,n)}return i}return df(e,t),e.prototype.clone=function(){return new e(this.flatCoordinates.slice(),void 0,this.layout)},e.prototype.closestPointXY=function(t,e,r,n){var i=this.flatCoordinates,o=t-i[0],a=e-i[1],s=o*o+a*a;if(s=e[0]||(t[1]<=e[1]&&t[3]>=e[1]||vt(t,this.intersectsCoordinate.bind(this)))}return!1},e.prototype.setCenter=function(t){var e=this.stride,r=this.flatCoordinates[e]-this.flatCoordinates[0],n=t.slice();n[e]=n[0]+r;for(var i=1;i=this.dragVertexDelay_?(this.downPx_=e.pixel,this.shouldHandle_=!this.freehand_,r=!0):this.lastDragTime_=void 0,this.shouldHandle_&&void 0!==this.downTimeout_&&(clearTimeout(this.downTimeout_),this.downTimeout_=void 0));return this.freehand_&&e.type===un.POINTERDRAG&&null!==this.sketchFeature_?(this.addToDrawing_(e),n=!1):this.freehand_&&e.type===un.POINTERDOWN?n=!1:r?(n=e.type===un.POINTERMOVE)&&this.freehand_?n=this.handlePointerMove_(e):("mouse"==e.pointerEvent.pointerType||e.type===un.POINTERDRAG&&void 0===this.downTimeout_)&&this.handlePointerMove_(e):e.type===un.DBLCLICK&&(n=!1),t.prototype.handleEvent.call(this,e)&&n},e.prototype.handleDownEvent=function(t){return this.shouldHandle_=!this.freehand_,this.freehand_?(this.downPx_=t.pixel,this.finishCoordinate_||this.startDrawing_(t),!0):this.condition_(t)?(this.lastDragTime_=Date.now(),this.downTimeout_=setTimeout(function(){this.handlePointerMove_(new hn(un.POINTERMOVE,t.map,t.pointerEvent,!1,t.frameState))}.bind(this),this.dragVertexDelay_),this.downPx_=t.pixel,!0):(this.lastDragTime_=void 0,!1)},e.prototype.handleUpEvent=function(t){var e=!0;this.downTimeout_&&(clearTimeout(this.downTimeout_),this.downTimeout_=void 0),this.handlePointerMove_(t);var r=this.mode_===Of.CIRCLE;return this.shouldHandle_?(this.finishCoordinate_?this.freehand_||r?this.finishDrawing():this.atFinish_(t)?this.finishCondition_(t)&&this.finishDrawing():this.addToDrawing_(t):(this.startDrawing_(t),this.mode_===Of.POINT&&this.finishDrawing()),e=!1):this.freehand_&&(this.finishCoordinate_=null,this.abortDrawing_()),!e&&this.stopClick_&&t.stopPropagation(),e},e.prototype.handlePointerMove_=function(t){if(this.downPx_&&(!this.freehand_&&this.shouldHandle_||this.freehand_&&!this.shouldHandle_)){var e=this.downPx_,r=t.pixel,n=e[0]-r[0],i=e[1]-r[1],o=n*n+i*i;if(this.shouldHandle_=this.freehand_?o>this.squaredClickTolerance_:o<=this.squaredClickTolerance_,!this.shouldHandle_)return!0}return this.finishCoordinate_?this.modifyDrawing_(t):this.createOrUpdateSketchPoint_(t),!0},e.prototype.atFinish_=function(t){var e=!1;if(this.sketchFeature_){var r=!1,n=[this.finishCoordinate_];if(this.mode_===Of.LINE_STRING)r=this.sketchCoords_.length>this.minPoints_;else if(this.mode_===Of.POLYGON){var i=this.sketchCoords_;r=i[0].length>this.minPoints_,n=[i[0][0],i[0][i[0].length-2]]}if(r)for(var o=t.map,a=0,s=n.length;a=this.maxPoints_&&(this.freehand_?r.pop():e=!0),r.push(n.slice()),this.geometryFunction_(r,i)):this.mode_===Of.POLYGON&&((r=this.sketchCoords_[0]).length>=this.maxPoints_&&(this.freehand_?r.pop():e=!0),r.push(n.slice()),e&&(this.finishCoordinate_=r[0]),this.geometryFunction_(this.sketchCoords_,i)),this.updateSketchFeatures_(),e&&this.finishDrawing()},e.prototype.removeLastPoint=function(){if(this.sketchFeature_){var t,e=this.sketchFeature_.getGeometry();this.mode_===Of.LINE_STRING?((t=this.sketchCoords_).splice(-2,1),this.geometryFunction_(t,e),t.length>=2&&(this.finishCoordinate_=t[t.length-2].slice())):this.mode_===Of.POLYGON&&((t=this.sketchCoords_[0]).splice(-2,1),this.sketchLine_.getGeometry().setCoordinates(t),this.geometryFunction_(this.sketchCoords_,e)),0===t.length&&(this.finishCoordinate_=null),this.updateSketchFeatures_()}},e.prototype.finishDrawing=function(){var t=this.abortDrawing_();if(t){var e=this.sketchCoords_,r=t.getGeometry();this.mode_===Of.LINE_STRING?(e.pop(),this.geometryFunction_(e,r)):this.mode_===Of.POLYGON&&(e[0].pop(),this.geometryFunction_(e,r),e=r.getCoordinates()),this.type_===Nt.MULTI_POINT?t.setGeometry(new Ef([e])):this.type_===Nt.MULTI_LINE_STRING?t.setGeometry(new vf([e])):this.type_===Nt.MULTI_POLYGON&&t.setGeometry(new wf([e])),this.dispatchEvent(new Pf(Cf,t)),this.features_&&this.features_.push(t),this.source_&&this.source_.addFeature(t)}},e.prototype.abortDrawing_=function(){this.finishCoordinate_=null;var t=this.sketchFeature_;return t&&(this.sketchFeature_=null,this.sketchPoint_=null,this.sketchLine_=null,this.overlay_.getSource().clear(!0)),t},e.prototype.extend=function(t){var e=t.getGeometry();this.sketchFeature_=t,this.sketchCoords_=e.getCoordinates();var r=this.sketchCoords_[this.sketchCoords_.length-1];this.finishCoordinate_=r.slice(),this.sketchCoords_.push(r.slice()),this.updateSketchFeatures_(),this.dispatchEvent(new Pf(Rf,this.sketchFeature_))},e.prototype.updateSketchFeatures_=function(){var t=[];this.sketchFeature_&&t.push(this.sketchFeature_),this.sketchLine_&&t.push(this.sketchLine_),this.sketchPoint_&&t.push(this.sketchPoint_);var e=this.overlay_.getSource();e.clear(!0),e.addFeatures(t)},e.prototype.updateState_=function(){var t=this.getMap(),e=this.getActive();t&&e||this.abortDrawing_(),this.overlay_.setMap(e?t:null)},e}(mo),bf=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Lf={EXTENTCHANGED:"extentchanged"},Mf=function(t){function e(e){var r=t.call(this,Lf.EXTENTCHANGED)||this;return r.extent=e,r}return bf(e,t),e}(M);function Ff(){var t=Du();return function(e,r){return t[Nt.POLYGON]}}function Af(){var t=Du();return function(e,r){return t[Nt.POINT]}}function Nf(t){return function(e){return $([t,e])}}function Df(t,e){return t[0]==e[0]?function(r){return $([t,[r[0],e[1]]])}:t[1]==e[1]?function(r){return $([t,[e[0],r[1]]])}:null}var Gf=function(t){function e(e){var r=this,n=e||{};return(r=t.call(this,n)||this).extent_=null,r.pointerHandler_=null,r.pixelTolerance_=void 0!==n.pixelTolerance?n.pixelTolerance:10,r.snappedToVertex_=!1,r.extentFeature_=null,r.vertexFeature_=null,e||(e={}),r.extentOverlay_=new Bp({source:new Dl({useSpatialIndex:!1,wrapX:!!e.wrapX}),style:e.boxStyle?e.boxStyle:Ff(),updateWhileAnimating:!0,updateWhileInteracting:!0}),r.vertexOverlay_=new Bp({source:new Dl({useSpatialIndex:!1,wrapX:!!e.wrapX}),style:e.pointerStyle?e.pointerStyle:Af(),updateWhileAnimating:!0,updateWhileInteracting:!0}),e.extent&&r.setExtent(e.extent),r}return bf(e,t),e.prototype.snapToVertex_=function(t,e){var r=e.getCoordinateFromPixelInternal(t),n=this.getExtentInternal();if(n){var i=function(t){return[[[t[0],t[1]],[t[0],t[3]]],[[t[0],t[3]],[t[2],t[3]]],[[t[2],t[3]],[t[2],t[1]]],[[t[2],t[1]],[t[0],t[1]]]]}(n);i.sort(function(t,e){return Qn(r,t)-Qn(r,e)});var o=i[0],a=Xn(r,o),s=e.getPixelFromCoordinateInternal(a);if(Jn(t,s)<=this.pixelTolerance_){var u=e.getPixelFromCoordinateInternal(o[0]),l=e.getPixelFromCoordinateInternal(o[1]),h=qn(s,u),c=qn(s,l),p=Math.sqrt(Math.min(h,c));return this.snappedToVertex_=p<=this.pixelTolerance_,this.snappedToVertex_&&(a=h>c?o[1]:o[0]),a}}return null},e.prototype.handlePointerMove_=function(t){var e=t.pixel,r=t.map,n=this.snapToVertex_(e,r);n||(n=r.getCoordinateFromPixelInternal(e)),this.createOrUpdatePointerFeature_(n)},e.prototype.createOrUpdateExtentFeature_=function(t){var e=this.extentFeature_;return e?t?e.setGeometry(zr(t)):e.setGeometry(void 0):(e=new q(t?zr(t):{}),this.extentFeature_=e,this.extentOverlay_.getSource().addFeature(e)),e},e.prototype.createOrUpdatePointerFeature_=function(t){var e=this.vertexFeature_;e?e.getGeometry().setCoordinates(t):(e=new q(new Or(t)),this.vertexFeature_=e,this.vertexOverlay_.getSource().addFeature(e));return e},e.prototype.handleEvent=function(e){return!e.pointerEvent||(e.type!=un.POINTERMOVE||this.handlingDownUpSequence||this.handlePointerMove_(e),t.prototype.handleEvent.call(this,e),!1)},e.prototype.handleDownEvent=function(t){var e=t.pixel,r=t.map,n=this.getExtentInternal(),i=this.snapToVertex_(e,r),o=function(t){var e=null,r=null;return t[0]==n[0]?e=n[2]:t[0]==n[2]&&(e=n[0]),t[1]==n[1]?r=n[3]:t[1]==n[3]&&(r=n[1]),null!==e&&null!==r?[e,r]:null};if(i&&n){var a=i[0]==n[0]||i[0]==n[2]?i[0]:null,s=i[1]==n[1]||i[1]==n[3]?i[1]:null;null!==a&&null!==s?this.pointerHandler_=Nf(o(i)):null!==a?this.pointerHandler_=Df(o([a,n[1]]),o([a,n[3]])):null!==s&&(this.pointerHandler_=Df(o([n[0],s]),o([n[2],s])))}else i=r.getCoordinateFromPixelInternal(e),this.setExtent([i[0],i[1],i[0],i[1]]),this.pointerHandler_=Nf(i);return!0},e.prototype.handleDragEvent=function(t){if(this.pointerHandler_){var e=t.coordinate;this.setExtent(this.pointerHandler_(e)),this.createOrUpdatePointerFeature_(e)}return!0},e.prototype.handleUpEvent=function(t){this.pointerHandler_=null;var e=this.getExtentInternal();return e&&0!==mt(e)||this.setExtent(null),!1},e.prototype.setMap=function(e){this.extentOverlay_.setMap(e),this.vertexOverlay_.setMap(e),t.prototype.setMap.call(this,e)},e.prototype.getExtent=function(){return Be(this.getExtentInternal(),this.getMap().getView().getProjection())},e.prototype.getExtentInternal=function(){return this.extent_},e.prototype.setExtent=function(t){this.extent_=t||null,this.createOrUpdateExtentFeature_(t),this.dispatchEvent(new Mf(this.extent_))},e}(mo),jf=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),kf=1,Uf=[0,0,0,0],Bf=[],Yf="modifystart",zf="modifyend",Xf=function(t){function e(e,r,n){var i=t.call(this,e)||this;return i.features=r,i.mapBrowserEvent=n,i}return jf(e,t),e}(M);function Vf(t,e){return t.index-e.index}function Wf(t,e,r){var n=e.geometry;if(n.getType()===Nt.CIRCLE){var i=n;if(e.index===kf){var o=qn(i.getCenter(),t),a=Math.sqrt(o)-i.getRadius();return a*a}}var s=Ue(t,r);return Bf[0]=Ue(e.segment[0],r),Bf[1]=Ue(e.segment[1],r),Qn(s,Bf)}function Zf(t,e,r){var n=e.geometry;if(n.getType()===Nt.CIRCLE&&e.index===kf)return n.getClosestPoint(t);var i=Ue(t,r);return Bf[0]=Ue(e.segment[0],r),Bf[1]=Ue(e.segment[1],r),ke(Xn(i,Bf),r)}function Kf(){var t=Du();return function(e,r){return t[Nt.POINT]}}var Hf=function(t){function e(e){var r,n=t.call(this,e)||this;if(n.boundHandleFeatureChange_=n.handleFeatureChange_.bind(n),n.condition_=e.condition?e.condition:go,n.defaultDeleteCondition_=function(t){return io(t)&&ho(t)},n.deleteCondition_=e.deleteCondition?e.deleteCondition:n.defaultDeleteCondition_,n.insertVertexCondition_=e.insertVertexCondition?e.insertVertexCondition:so,n.vertexFeature_=null,n.vertexSegments_=null,n.lastPixel_=[0,0],n.ignoreNextSingleClick_=!1,n.modified_=!1,n.rBush_=new Fl,n.pixelTolerance_=void 0!==e.pixelTolerance?e.pixelTolerance:10,n.snappedToVertex_=!1,n.changingFeature_=!1,n.dragSegments_=[],n.overlay_=new Bp({source:new Dl({useSpatialIndex:!1,wrapX:!!e.wrapX}),style:e.style?e.style:Kf(),updateWhileAnimating:!0,updateWhileInteracting:!0}),n.SEGMENT_WRITERS_={Point:n.writePointGeometry_.bind(n),LineString:n.writeLineStringGeometry_.bind(n),LinearRing:n.writeLineStringGeometry_.bind(n),Polygon:n.writePolygonGeometry_.bind(n),MultiPoint:n.writeMultiPointGeometry_.bind(n),MultiLineString:n.writeMultiLineStringGeometry_.bind(n),MultiPolygon:n.writeMultiPolygonGeometry_.bind(n),Circle:n.writeCircleGeometry_.bind(n),GeometryCollection:n.writeGeometryCollectionGeometry_.bind(n)},n.source_=null,e.source?(n.source_=e.source,r=new Z(n.source_.getFeatures()),n.source_.addEventListener(bl.ADDFEATURE,n.handleSourceAdd_.bind(n)),n.source_.addEventListener(bl.REMOVEFEATURE,n.handleSourceRemove_.bind(n))):r=e.features,!r)throw new Error("The modify interaction requires features or a source");return n.features_=r,n.features_.forEach(n.addFeature_.bind(n)),n.features_.addEventListener(h.ADD,n.handleFeatureAdd_.bind(n)),n.features_.addEventListener(h.REMOVE,n.handleFeatureRemove_.bind(n)),n.lastPointerEvent_=null,n}return jf(e,t),e.prototype.addFeature_=function(t){var e=t.getGeometry();if(e){var r=this.SEGMENT_WRITERS_[e.getType()];r&&r(t,e)}var n=this.getMap();n&&n.isRendered()&&this.getActive()&&this.handlePointerAtPixel_(this.lastPixel_,n),t.addEventListener(N.CHANGE,this.boundHandleFeatureChange_)},e.prototype.willModifyFeatures_=function(t){this.modified_||(this.modified_=!0,this.dispatchEvent(new Xf(Yf,this.features_,t)))},e.prototype.removeFeature_=function(t){this.removeFeatureSegmentData_(t),this.vertexFeature_&&0===this.features_.getLength()&&(this.overlay_.getSource().removeFeature(this.vertexFeature_),this.vertexFeature_=null),t.removeEventListener(N.CHANGE,this.boundHandleFeatureChange_)},e.prototype.removeFeatureSegmentData_=function(t){var e=this.rBush_,r=[];e.forEach(function(e){t===e.feature&&r.push(e)});for(var n=r.length-1;n>=0;--n){for(var i=r[n],o=this.dragSegments_.length-1;o>=0;--o)this.dragSegments_[o][0]===i&&this.dragSegments_.splice(o,1);e.remove(i)}},e.prototype.setActive=function(e){this.vertexFeature_&&!e&&(this.overlay_.getSource().removeFeature(this.vertexFeature_),this.vertexFeature_=null),t.prototype.setActive.call(this,e)},e.prototype.setMap=function(e){this.overlay_.setMap(e),t.prototype.setMap.call(this,e)},e.prototype.getOverlay=function(){return this.overlay_},e.prototype.handleSourceAdd_=function(t){t.feature&&this.features_.push(t.feature)},e.prototype.handleSourceRemove_=function(t){t.feature&&this.features_.remove(t.feature)},e.prototype.handleFeatureAdd_=function(t){this.addFeature_(t.element)},e.prototype.handleFeatureChange_=function(t){if(!this.changingFeature_){var e=t.target;this.removeFeature_(e),this.addFeature_(e)}},e.prototype.handleFeatureRemove_=function(t){var e=t.element;this.removeFeature_(e)},e.prototype.writePointGeometry_=function(t,e){var r=e.getCoordinates(),n={feature:t,geometry:e,segment:[r,r]};this.rBush_.insert(e.getExtent(),n)},e.prototype.writeMultiPointGeometry_=function(t,e){for(var r=e.getCoordinates(),n=0,i=r.length;n=0;--g)this.insertVertex_.apply(this,i[g])}return!!this.vertexFeature_},e.prototype.handleUpEvent=function(t){for(var e=this.dragSegments_.length-1;e>=0;--e){var r=this.dragSegments_[e][0],n=r.geometry;if(n.getType()===Nt.CIRCLE){var i=n.getCenter(),o=r.featureSegments[0],a=r.featureSegments[1];o.segment[0]=i,o.segment[1]=i,a.segment[0]=i,a.segment[1]=i,this.rBush_.update(ht(i),o),this.rBush_.update(n.getExtent(),a)}else this.rBush_.update($(r.segment),r)}return this.modified_&&(this.dispatchEvent(new Xf(zf,this.features_,t)),this.modified_=!1),!1},e.prototype.handlePointerMove_=function(t){this.lastPixel_=t.pixel,this.handlePointerAtPixel_(t.pixel,t.map)},e.prototype.handlePointerAtPixel_=function(t,e){var r=e.getCoordinateFromPixel(t),n=e.getView().getProjection(),i=Be(tt(Ye(ht(r,Uf),n),e.getView().getResolution()*this.pixelTolerance_,Uf),n),a=this.rBush_.getInExtent(i);if(a.length>0){a.sort(function(t,e){return Wf(r,t,n)-Wf(r,e,n)});var s=a[0],u=s.segment,l=Zf(r,s,n),h=e.getPixelFromCoordinate(l),c=Jn(t,h);if(c<=this.pixelTolerance_){var p={};if(s.geometry.getType()===Nt.CIRCLE&&s.index===kf)this.snappedToVertex_=!0,this.createOrUpdateVertexFeature_(l);else{var f=e.getPixelFromCoordinate(u[0]),d=e.getPixelFromCoordinate(u[1]),_=qn(h,f),g=qn(h,d);c=Math.sqrt(Math.min(_,g)),this.snappedToVertex_=c<=this.pixelTolerance_,this.snappedToVertex_&&(l=_>g?u[1]:u[0]),this.createOrUpdateVertexFeature_(l);for(var y=1,v=a.length;y=0;--i)c=o((h=(r=p[i])[0]).feature),h.depth&&(c+="-"+h.depth.join("-")),c in f||(f[c]={}),0===r[1]?(f[c].right=h,f[c].index=h.index):1==r[1]&&(f[c].left=h,f[c].index=h.index+1);for(c in f){switch(l=f[c].right,s=f[c].left,(u=(a=f[c].index)-1)<0&&(u=0),t=e=(n=(h=void 0!==s?s:l).geometry).getCoordinates(),d=!1,n.getType()){case Nt.MULTI_LINE_STRING:e[h.depth[0]].length>2&&(e[h.depth[0]].splice(a,1),d=!0);break;case Nt.LINE_STRING:e.length>2&&(e.splice(a,1),d=!0);break;case Nt.MULTI_POLYGON:t=t[h.depth[1]];case Nt.POLYGON:(t=t[h.depth[0]]).length>4&&(a==t.length-1&&(a=0),t.splice(a,1),d=!0,0===a&&(t.pop(),t.push(t[0]),u=t.length-1))}if(d){this.setGeometryCoordinates_(n,e);var _=[];if(void 0!==s&&(this.rBush_.remove(s),_.push(s.segment[0])),void 0!==l&&(this.rBush_.remove(l),_.push(l.segment[1])),void 0!==s&&void 0!==l){var g={depth:h.depth,feature:h.feature,geometry:h.geometry,index:u,segment:_};this.rBush_.insert($(g.segment),g)}this.updateSegmentIndices_(n,a,h.depth,-1),this.vertexFeature_&&(this.overlay_.getSource().removeFeature(this.vertexFeature_),this.vertexFeature_=null),p.length=0}}return d},e.prototype.setGeometryCoordinates_=function(t,e){this.changingFeature_=!0,t.setCoordinates(e),this.changingFeature_=!1},e.prototype.updateSegmentIndices_=function(t,e,r,n){this.rBush_.forEachInExtent(t.getExtent(),function(i){i.geometry===t&&(void 0===r||void 0===i.depth||R(i.depth,r))&&i.index>e&&(i.index+=n)})},e}(mo),qf=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Jf={SELECT:"select"},Qf=function(t){function e(e,r,n,i){var o=t.call(this,e)||this;return o.selected=r,o.deselected=n,o.mapBrowserEvent=i,o}return qf(e,t),e}(M);function $f(t){if(!this.condition_(t))return!0;var e=this.addCondition_(t),r=this.removeCondition_(t),n=this.toggleCondition_(t),i=!e&&!r&&!n,o=t.map,a=this.getFeatures(),s=[],u=[];if(i){f(this.featureLayerAssociation_),o.forEachFeatureAtPixel(t.pixel,function(t,e){if(this.filter_(t,e))return u.push(t),this.addFeatureLayerAssociation_(t,e),!this.multi_}.bind(this),{layerFilter:this.layerFilter_,hitTolerance:this.hitTolerance_});for(var l=a.getLength()-1;l>=0;--l){var h=a.item(l),c=u.indexOf(h);c>-1?u.splice(c,1):(a.remove(h),s.push(h))}0!==u.length&&a.extend(u)}else{o.forEachFeatureAtPixel(t.pixel,function(t,i){if(this.filter_(t,i))return!e&&!n||T(a.getArray(),t)?(r||n)&&T(a.getArray(),t)&&(s.push(t),this.removeFeatureLayerAssociation_(t)):(u.push(t),this.addFeatureLayerAssociation_(t,i)),!this.multi_}.bind(this),{layerFilter:this.layerFilter_,hitTolerance:this.hitTolerance_});for(var p=s.length-1;p>=0;--p)a.remove(s[p]);a.extend(u)}return(u.length>0||s.length>0)&&this.dispatchEvent(new Qf(Jf.SELECT,u,s,t)),!0}var td=function(t){function e(e){var r,n,i=t.call(this,{handleEvent:$f})||this,o=e||{};if(i.condition_=o.condition?o.condition:ho,i.addCondition_=o.addCondition?o.addCondition:lo,i.removeCondition_=o.removeCondition?o.removeCondition:lo,i.toggleCondition_=o.toggleCondition?o.toggleCondition:po,i.multi_=!!o.multi&&o.multi,i.filter_=o.filter?o.filter:P,i.hitTolerance_=o.hitTolerance?o.hitTolerance:0,i.style_=void 0!==o.style?o.style:(x((r=Du())[Nt.POLYGON],r[Nt.LINE_STRING]),x(r[Nt.GEOMETRY_COLLECTION],r[Nt.LINE_STRING]),function(t){return t.getGeometry()?r[t.getGeometry().getType()]:null}),i.featureStyleAssociation_={},i.features_=o.features||new Z,o.layers)if("function"==typeof o.layers)n=o.layers;else{var a=o.layers;n=function(t){return T(a,t)}}else n=P;i.layerFilter_=n,i.featureLayerAssociation_={};var s=i.getFeatures();return s.addEventListener(h.ADD,i.addFeature_.bind(i)),s.addEventListener(h.REMOVE,i.removeFeature_.bind(i)),i}return qf(e,t),e.prototype.addFeatureLayerAssociation_=function(t,e){this.featureLayerAssociation_[o(t)]=e},e.prototype.getFeatures=function(){return this.features_},e.prototype.getHitTolerance=function(){return this.hitTolerance_},e.prototype.getLayer=function(t){return this.featureLayerAssociation_[o(t)]},e.prototype.setHitTolerance=function(t){this.hitTolerance_=t},e.prototype.setMap=function(e){this.getMap()&&this.style_&&this.features_.forEach(this.removeSelectedStyle_.bind(this)),t.prototype.setMap.call(this,e),e&&this.style_&&this.features_.forEach(this.giveSelectedStyle_.bind(this))},e.prototype.addFeature_=function(t){var e=t.element;this.style_&&this.giveSelectedStyle_(e)},e.prototype.removeFeature_=function(t){var e=t.element;this.style_&&this.removeSelectedStyle_(e)},e.prototype.giveSelectedStyle_=function(t){var e=o(t);this.featureStyleAssociation_[e]=t.getStyle(),t.setStyle(this.style_)},e.prototype.removeSelectedStyle_=function(t){var e=o(t);t.setStyle(this.featureStyleAssociation_[e]),delete this.featureStyleAssociation_[e]},e.prototype.removeFeatureLayerAssociation_=function(t){delete this.featureLayerAssociation_[o(t)]},e}(to),ed=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}();function rd(t){return t.feature?t.feature:t.element?t.element:void 0}var nd=[],id=function(t){function e(e){var r=this,n=e||{},i=n;return i.handleDownEvent||(i.handleDownEvent=P),i.stopDown||(i.stopDown=I),(r=t.call(this,i)||this).source_=n.source?n.source:null,r.vertex_=void 0===n.vertex||n.vertex,r.edge_=void 0===n.edge||n.edge,r.features_=n.features?n.features:null,r.featuresListenerKeys_=[],r.featureChangeListenerKeys_={},r.indexedFeaturesExtents_={},r.pendingFeatures_={},r.pixelTolerance_=void 0!==n.pixelTolerance?n.pixelTolerance:10,r.rBush_=new Fl,r.SEGMENT_WRITERS_={Point:r.writePointGeometry_.bind(r),LineString:r.writeLineStringGeometry_.bind(r),LinearRing:r.writeLineStringGeometry_.bind(r),Polygon:r.writePolygonGeometry_.bind(r),MultiPoint:r.writeMultiPointGeometry_.bind(r),MultiLineString:r.writeMultiLineStringGeometry_.bind(r),MultiPolygon:r.writeMultiPolygonGeometry_.bind(r),GeometryCollection:r.writeGeometryCollectionGeometry_.bind(r),Circle:r.writeCircleGeometry_.bind(r)},r}return ed(e,t),e.prototype.addFeature=function(t,e){var r=void 0===e||e,n=o(t),i=t.getGeometry();if(i){var a=this.SEGMENT_WRITERS_[i.getType()];a&&(this.indexedFeaturesExtents_[n]=i.getExtent([1/0,1/0,-1/0,-1/0]),a(t,i))}r&&(this.featureChangeListenerKeys_[n]=g(t,N.CHANGE,this.handleFeatureChange_,this))},e.prototype.forEachFeatureAdd_=function(t){this.addFeature(t)},e.prototype.forEachFeatureRemove_=function(t){this.removeFeature(t)},e.prototype.getFeatures_=function(){var t;return this.features_?t=this.features_:this.source_&&(t=this.source_.getFeatures()),t},e.prototype.handleEvent=function(e){var r=this.snapTo(e.pixel,e.coordinate,e.map);return r.snapped&&(e.coordinate=r.vertex.slice(0,2),e.pixel=r.vertexPixel),t.prototype.handleEvent.call(this,e)},e.prototype.handleFeatureAdd_=function(t){var e=rd(t);this.addFeature(e)},e.prototype.handleFeatureRemove_=function(t){var e=rd(t);this.removeFeature(e)},e.prototype.handleFeatureChange_=function(t){var e=t.target;if(this.handlingDownUpSequence){var r=o(e);r in this.pendingFeatures_||(this.pendingFeatures_[r]=e)}else this.updateFeature_(e)},e.prototype.handleUpEvent=function(t){var e=d(this.pendingFeatures_);return e.length&&(e.forEach(this.updateFeature_.bind(this)),this.pendingFeatures_={}),!1},e.prototype.removeFeature=function(t,e){var r=void 0===e||e,n=o(t),i=this.indexedFeaturesExtents_[n];if(i){var a=this.rBush_,s=[];a.forEachInExtent(i,function(e){t===e.feature&&s.push(e)});for(var u=s.length-1;u>=0;--u)a.remove(s[u])}r&&(v(this.featureChangeListenerKeys_[n]),delete this.featureChangeListenerKeys_[n])},e.prototype.setMap=function(e){var r=this.getMap(),n=this.featuresListenerKeys_,i=this.getFeatures_();r&&(n.forEach(v),n.length=0,i.forEach(this.forEachFeatureRemove_.bind(this))),t.prototype.setMap.call(this,e),e&&(this.features_?n.push(g(this.features_,h.ADD,this.handleFeatureAdd_,this),g(this.features_,h.REMOVE,this.handleFeatureRemove_,this)):this.source_&&n.push(g(this.source_,bl.ADDFEATURE,this.handleFeatureAdd_,this),g(this.source_,bl.REMOVEFEATURE,this.handleFeatureRemove_,this)),i.forEach(this.forEachFeatureAdd_.bind(this)))},e.prototype.snapTo=function(t,e,r){var n=$([r.getCoordinateFromPixel([t[0]-this.pixelTolerance_,t[1]+this.pixelTolerance_]),r.getCoordinateFromPixel([t[0]+this.pixelTolerance_,t[1]-this.pixelTolerance_])]),i=this.rBush_.getInExtent(n);this.vertex_&&!this.edge_&&(i=i.filter(function(t){return t.feature.getGeometry().getType()!==Nt.CIRCLE}));var o=!1,a=null,s=null;if(0===i.length)return{snapped:o,vertex:a,vertexPixel:s};for(var u,l=r.getView().getProjection(),h=Ue(e,l),c=1/0,p=0;pm?_[1]:_[0],s=r.getPixelFromCoordinate(a))}else if(this.edge_){var E=u.feature.getGeometry().getType()===Nt.CIRCLE;if(E?a=function(t,e){var r=e.getRadius(),n=e.getCenter(),i=n[0],o=n[1],a=t[0]-i,s=t[1]-o;0===a&&0===s&&(a=1);var u=Math.sqrt(a*a+s*s);return[i+r*a/u,o+r*s/u]}(e,u.feature.getGeometry()):(nd[0]=Ue(_[0],l),nd[1]=Ue(_[1],l),a=ke(Xn(h,nd),l)),Jn(t,s=r.getPixelFromCoordinate(a))<=this.pixelTolerance_&&(o=!0,this.vertex_&&!E)){g=r.getPixelFromCoordinate(_[0]),y=r.getPixelFromCoordinate(_[1]),v=qn(s,g),m=qn(s,y);Math.sqrt(Math.min(v,m))<=this.pixelTolerance_&&(a=v>m?_[1]:_[0],s=r.getPixelFromCoordinate(a))}}return o&&(s=[Math.round(s[0]),Math.round(s[1])]),{snapped:o,vertex:a,vertexPixel:s}},e.prototype.updateFeature_=function(t){this.removeFeature(t,!1),this.addFeature(t,!1)},e.prototype.writeCircleGeometry_=function(t,e){for(var r=Xr(e).getCoordinates()[0],n=0,i=r.length-1;n=0;r--){var l=o[r][0];if(it(new wr(l).getExtent(),new wr(s).getExtent())){o[r].push(s),u=!0;break}}u||o.push([s.reverse()])}return o}(o.rings,a);1===s.length?(i=Nt.POLYGON,t=Object.assign({},t,((r={}).rings=s[0],r))):(i=Nt.MULTI_POLYGON,t=Object.assign({},t,((n={}).rings=s,n)))}return _d((0,Td[i])(t),!1,e)}function xd(t){var e=At.XY;return!0===t.hasZ&&!0===t.hasM?e=At.XYZM:!0===t.hasZ?e=At.XYZ:!0===t.hasM&&(e=At.XYM),e}function Od(t){var e=t.getLayout();return{hasZ:e===At.XYZ||e===At.XYZM,hasM:e===At.XYM||e===At.XYZM}}function Rd(t,e){return(0,Sd[t.getType()])(_d(t,!0,e),e)}Sd[Nt.POINT]=function(t,e){var r,n=t.getCoordinates(),i=t.getLayout();i===At.XYZ?r={x:n[0],y:n[1],z:n[2]}:i===At.XYM?r={x:n[0],y:n[1],m:n[2]}:i===At.XYZM?r={x:n[0],y:n[1],z:n[2],m:n[3]}:i===At.XY?r={x:n[0],y:n[1]}:K(!1,34);return r},Sd[Nt.LINE_STRING]=function(t,e){var r=Od(t);return{hasZ:r.hasZ,hasM:r.hasM,paths:[t.getCoordinates()]}},Sd[Nt.POLYGON]=function(t,e){var r=Od(t);return{hasZ:r.hasZ,hasM:r.hasM,rings:t.getCoordinates(!1)}},Sd[Nt.MULTI_POINT]=function(t,e){var r=Od(t);return{hasZ:r.hasZ,hasM:r.hasM,points:t.getCoordinates()}},Sd[Nt.MULTI_LINE_STRING]=function(t,e){var r=Od(t);return{hasZ:r.hasZ,hasM:r.hasM,paths:t.getCoordinates()}},Sd[Nt.MULTI_POLYGON]=function(t,e){for(var r=Od(t),n=t.getCoordinates(!1),i=[],o=0;o=0;a--)i.push(n[o][a]);return{hasZ:r.hasZ,hasM:r.hasM,rings:i}};var Cd=function(t){function e(e){var r=this,n=e||{};return(r=t.call(this)||this).geometryName_=n.geometryName,r}return Ed(e,t),e.prototype.readFeatureFromObject=function(t,e){var r=t,n=wd(r.geometry,e),i=new q;return this.geometryName_&&i.setGeometryName(this.geometryName_),i.setGeometry(n),e&&e.idField&&r.attributes[e.idField]&&i.setId(r.attributes[e.idField]),r.attributes&&i.setProperties(r.attributes,!0),i},e.prototype.readFeaturesFromObject=function(t,e){var r=e||{};if(t.features){var n=[],i=t.features;r.idField=t.objectIdFieldName;for(var o=0,a=i.length;o0?r[0]:null},e.prototype.readFeatureFromNode=function(t,e){return null},e.prototype.readFeatures=function(t,e){if(t){if("string"==typeof t){var r=Fs(t);return this.readFeaturesFromDocument(r,e)}return Ms(t)?this.readFeaturesFromDocument(t,e):this.readFeaturesFromNode(t,e)}return[]},e.prototype.readFeaturesFromDocument=function(t,e){for(var r=[],n=t.firstChild;n;n=n.nextSibling)n.nodeType==Node.ELEMENT_NODE&&x(r,this.readFeaturesFromNode(n,e));return r},e.prototype.readFeaturesFromNode=function(t,e){return n()},e.prototype.readGeometry=function(t,e){if(t){if("string"==typeof t){var r=Fs(t);return this.readGeometryFromDocument(r,e)}return Ms(t)?this.readGeometryFromDocument(t,e):this.readGeometryFromNode(t,e)}return null},e.prototype.readGeometryFromDocument=function(t,e){return null},e.prototype.readGeometryFromNode=function(t,e){return null},e.prototype.readProjection=function(t){if(t){if("string"==typeof t){var e=Fs(t);return this.readProjectionFromDocument(e)}return Ms(t)?this.readProjectionFromDocument(t):this.readProjectionFromNode(t)}return null},e.prototype.readProjectionFromDocument=function(t){return this.dataProjection},e.prototype.readProjectionFromNode=function(t){return this.dataProjection},e.prototype.writeFeature=function(t,e){var r=this.writeFeatureNode(t,e);return this.xmlSerializer_.serializeToString(r)},e.prototype.writeFeatureNode=function(t,e){return null},e.prototype.writeFeatures=function(t,e){var r=this.writeFeaturesNode(t,e);return this.xmlSerializer_.serializeToString(r)},e.prototype.writeFeaturesNode=function(t,e){return null},e.prototype.writeGeometry=function(t,e){var r=this.writeGeometryNode(t,e);return this.xmlSerializer_.serializeToString(r)},e.prototype.writeGeometryNode=function(t,e){return null},e}(dd),bd=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Ld="http://www.opengis.net/gml",Md=/^[\s\xa0]*$/,Fd=function(t){function e(e){var r=t.call(this)||this,n=e||{};return r.featureType=n.featureType,r.featureNS=n.featureNS,r.srsName=n.srsName,r.schemaLocation="",r.FEATURE_COLLECTION_PARSERS={},r.FEATURE_COLLECTION_PARSERS[r.namespace]={featureMember:Ns(r.readFeaturesInternal),featureMembers:Ds(r.readFeaturesInternal)},r}return bd(e,t),e.prototype.readFeaturesInternal=function(t,e){var r=t.localName,n=null;if("FeatureCollection"==r)n=Ws([],this.FEATURE_COLLECTION_PARSERS,t,e,this);else if("featureMembers"==r||"featureMember"==r){var i=e[0],o=i.featureType,a=i.featureNS;if(!o&&t.childNodes){o=[],a={};for(var s=0,u=t.childNodes.length;s0){i[s]={_content_:i[s]};for(var l=0;l1?o.Document=t:1==t.length&&(o.Placemark=t[0]);var a=mg[r.namespaceURI],s=zs(o,a);return Ks(i,Eg,Ys,s,[e],a,this),r},e}(Id);function bg(t,e){var r=null,n=[0,0],i="start";if(t.getImage()){var o=t.getImage().getImageSize();if(null===o&&(o=tg),2==o.length){var a=t.getImage().getScale();n[0]=a*o[0]/2,n[1]=-a*o[1]/2,i="left"}}if(null!==t.getText()){var s=t.getText();(r=s.clone()).setFont(s.getFont()||Rg.getFont()),r.setScale(s.getScale()||Rg.getScale()),r.setFill(s.getFill()||Rg.getFill()),r.setStroke(s.getStroke()||xg)}else r=Rg.clone();return r.setText(e),r.setOffsetX(n[0]),r.setOffsetY(n[1]),r.setTextAlign(i),new ju({text:r})}function Lg(t){var e=Ls(t,!1),r=/^\s*#?\s*([0-9A-Fa-f]{8})\s*$/.exec(e);if(r){var n=r[1];return[parseInt(n.substr(6,2),16),parseInt(n.substr(4,2),16),parseInt(n.substr(2,2),16),parseInt(n.substr(0,2),16)/255]}}function Mg(t){for(var e,r=Ls(t,!1),n=[],i=/^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)(?:\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?))?\s*/i;e=i.exec(r);){var o=parseFloat(e[1]),a=parseFloat(e[2]),s=e[3]?parseFloat(e[3]):0;n.push(o,a,s),r=r.substr(e[0].length)}if(""===r)return n}function Fg(t){var e=Ls(t,!1).trim(),r=t.baseURI;return r&&"about:blank"!=r||(r=window.location.href),r?new URL(e,r).href:e}function Ag(t){return jd(t)}var Ng=Xs(fg,{Pair:function(t,e){var r=Ws({},hy,t,e);if(!r)return;var n=r.key;if(n&&"normal"==n){var i=r.styleUrl;i&&(e[e.length-1]=i);var o=r.Style;o&&(e[e.length-1]=o)}}});function Dg(t,e){return Ws(void 0,Ng,t,e)}var Gg=Xs(fg,{Icon:js(function(t,e){var r=Ws({},Wg,t,e);return r||null}),heading:js(jd),hotSpot:js(function(t){var e,r=t.getAttribute("xunits"),n=t.getAttribute("yunits");return e="insetPixels"!==r?"insetPixels"!==n?Iu.BOTTOM_LEFT:Iu.TOP_LEFT:"insetPixels"!==n?Iu.BOTTOM_RIGHT:Iu.TOP_RIGHT,{x:parseFloat(t.getAttribute("x")),xunits:dg[r],y:parseFloat(t.getAttribute("y")),yunits:dg[n],origin:e}}),scale:js(Ag)});var jg=Xs(fg,{color:js(Lg),scale:js(Ag)});var kg=Xs(fg,{color:js(Lg),width:js(jd)});var Ug=Xs(fg,{color:js(Lg),fill:js(Nd),outline:js(Nd)});var Bg=Xs(fg,{coordinates:Ds(Mg)});function Yg(t,e){return Ws(null,Bg,t,e)}var zg=Xs(pg,{Track:Ns(Vg)});var Xg=Xs(fg,{when:function(t,e){var r=e[e.length-1].whens,n=Ls(t,!1),i=Date.parse(n);r.push(isNaN(i)?0:i)}},Xs(pg,{coord:function(t,e){var r=e[e.length-1].flatCoordinates,n=Ls(t,!1),i=/^\s*([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s*$/i.exec(n);if(i){var o=parseFloat(i[1]),a=parseFloat(i[2]),s=parseFloat(i[3]);r.push(o,a,s,0)}else r.push(0,0,0,0)}}));function Vg(t,e){var r=Ws({flatCoordinates:[],whens:[]},Xg,t,e);if(r){for(var n=r.flatCoordinates,i=r.whens,o=0,a=Math.min(n.length,i.length);o0,h=u.href;h?n=h:l&&(n=eg);var c,p=Iu.BOTTOM_LEFT,f=r.hotSpot;f?(i=[f.x,f.y],o=f.xunits,a=f.yunits,p=f.origin):n===eg?(i=J_,o=Q_,a=$_):/^http:\/\/maps\.(?:google|gstatic)\.com\//.test(n)&&(i=[.5,0],o=Tu.FRACTION,a=Tu.FRACTION);var d,_=u.x,g=u.y;void 0!==_&&void 0!==g&&(c=[_,g]);var y,v=u.w,m=u.h;void 0!==v&&void 0!==m&&(d=[v,m]);var E=r.heading;void 0!==E&&(y=Xt(E));var T=r.scale;if(l){n==eg&&(d=tg,void 0===T&&(T=rg));var S=new Lu({anchor:i,anchorOrigin:p,anchorXUnits:o,anchorYUnits:a,crossOrigin:"anonymous",offset:c,offsetOrigin:Iu.BOTTOM_LEFT,rotation:y,scale:T,size:d,src:n});s.imageStyle=S}else s.imageStyle=Sg}},LabelStyle:function(t,e){var r=Ws({},jg,t,e);if(r){var n=e[e.length-1],i=new Bu({fill:new Eu({color:"color"in r?r.color:q_}),scale:r.scale});n.textStyle=i}},LineStyle:function(t,e){var r=Ws({},kg,t,e);if(r){var n=e[e.length-1],i=new Mu({color:"color"in r?r.color:q_,width:"width"in r?r.width:1});n.strokeStyle=i}},PolyStyle:function(t,e){var r=Ws({},Ug,t,e);if(r){var n=e[e.length-1],i=new Eu({color:"color"in r?r.color:q_});n.fillStyle=i;var o=r.fill;void 0!==o&&(n.fill=o);var a=r.outline;void 0!==a&&(n.outline=a)}}});function iy(t,e){var r=Ws({},ny,t,e);if(!r)return null;var n,i="fillStyle"in r?r.fillStyle:Tg,o=r.fill;void 0===o||o||(i=null),"imageStyle"in r?r.imageStyle!=Sg&&(n=r.imageStyle):n=wg;var a="textStyle"in r?r.textStyle:Rg,s="strokeStyle"in r?r.strokeStyle:Og,u=r.outline;return void 0===u||u||(s=null),[new ju({fill:i,image:n,stroke:s,text:a,zIndex:void 0})]}function oy(t,e){var r,n,i,o=e.length,a=new Array(e.length),s=new Array(e.length),u=new Array(e.length);r=!1,n=!1,i=!1;for(var l=0;l0){var d=zs(i,a);Ks(n,Uy,Yy,[{names:a,values:d}],r)}var _=r[0],g=e.getGeometry();g&&(g=_d(g,!0,_)),Ks(n,Uy,Ly,[g],r)}var Xy=Xs(fg,["extrude","tessellate","altitudeMode","coordinates"]),Vy=Xs(fg,{extrude:ks(zd),tessellate:ks(zd),altitudeMode:ks(Wd),coordinates:ks(function(t,e,r){var n,i=r[r.length-1],o=i.layout,a=i.stride;o==At.XY||o==At.XYM?n=2:o==At.XYZ||o==At.XYZM?n=3:K(!1,34);var s=e.length,u="";if(s>0){u+=e[0];for(var l=1;l>3)?r.readString():2===t?r.readFloat():3===t?r.readDouble():4===t?r.readVarint64():5===t?r.readVarint():6===t?r.readSVarint():7===t?r.readBoolean():null;e.values.push(n)}}function cv(t,e,r){if(1==t)e.id=r.readVarint();else if(2==t)for(var n=r.readVarint()+r.pos;r.pos>3}a--,1===o||2===o?(s+=t.readSVarint(),u+=t.readSVarint(),1===o&&l>h&&(n.push(l),h=l),r.push(s,u),l+=2):7===o?l>h&&(r.push(r[h],r[h+1]),l+=2):K(!1,59)}l>h&&(n.push(l),h=l)},e.prototype.createFeature_=function(t,e,r){var n,i=e.type;if(0===i)return null;var o,a=e.properties;this.idProperty_?(o=a[this.idProperty_],delete a[this.idProperty_]):o=e.id,a[this.layerName_]=e.layer.name;var s=[],u=[];this.readRawGeometry_(t,e,s,u);var l=function(t,e){var r;1===t?r=1===e?Nt.POINT:Nt.MULTI_POINT:2===t?r=1===e?Nt.LINE_STRING:Nt.MULTI_LINE_STRING:3===t&&(r=Nt.POLYGON);return r}(i,u.length);if(this.featureClass_===sv)(n=new this.featureClass_(l,s,u,a,o)).transform(r.dataProjection,r.featureProjection);else{var h=void 0;if(l==Nt.POLYGON){for(var c=[],p=0,f=0,d=0,_=u.length;d<_;++d){var g=u[d];Nr(s,p,g,2)||(c.push(u.slice(f,d)),f=d),p=g}h=c.length>1?new wf(s,At.XY,c):new Br(s,At.XY,u)}else h=l===Nt.POINT?new Or(s,At.XY):l===Nt.LINE_STRING?new Vp(s,At.XY):l===Nt.POLYGON?new Br(s,At.XY,u):l===Nt.MULTI_POINT?new Ef(s,At.XY):l===Nt.MULTI_LINE_STRING?new vf(s,At.XY,u):null;n=new(0,this.featureClass_),this.geometryName_&&n.setGeometryName(this.geometryName_);var y=_d(h,!1,r);n.setGeometry(y),n.setId(o),n.setProperties(a,!0)}return n},e.prototype.getType=function(){return rs.ARRAY_BUFFER},e.prototype.readFeatures=function(t,e){var r=this.layers_,n=this.adaptOptions(e),i=we(n.dataProjection);i.setWorldExtent(n.extent),n.dataProjection=i;var o=new iv.a(t),a=o.readFields(lv,{}),s=[];for(var u in a)if(!r||-1!=r.indexOf(u)){var l=a[u],h=l?[0,0,l.extent,l.extent]:null;i.setExtent(h);for(var c=0,p=l.length;c>1):i>>1}return e}(t),i=0,o=n.length;i=32;)e=63+(32|31&t),r+=String.fromCharCode(e),t>>=5;return e=t+63,r+=String.fromCharCode(e)}var Iv=function(t){function e(e){var r=t.call(this)||this,n=e||{};return r.dataProjection=we("EPSG:4326"),r.factor_=n.factor?n.factor:1e5,r.geometryLayout_=n.geometryLayout?n.geometryLayout:At.XY,r}return wv(e,t),e.prototype.readFeatureFromText=function(t,e){var r=this.readGeometryFromText(t,e);return new q(r)},e.prototype.readFeaturesFromText=function(t,e){return[this.readFeatureFromText(t,e)]},e.prototype.readGeometryFromText=function(t,e){var r=rr(this.geometryLayout_),n=Ov(t,r,this.factor_);Sv(n,0,n.length,r,n);var i=dr(n,0,n.length,r);return _d(new Vp(i,this.geometryLayout_),!1,this.adaptOptions(e))},e.prototype.writeFeatureText=function(t,e){var r=t.getGeometry();return r?this.writeGeometryText(r,e):(K(!1,40),"")},e.prototype.writeFeaturesText=function(t,e){return this.writeFeatureText(t[0],e)},e.prototype.writeGeometryText=function(t,e){var r=(t=_d(t,!0,this.adaptOptions(e))).getFlatCoordinates(),n=t.getStride();return Sv(r,0,r.length,n,r),xv(r,n,this.factor_)},e}(ng),bv=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Lv=function(t){function e(e){var r=t.call(this)||this,n=e||{};return r.layerName_=n.layerName,r.layers_=n.layers?n.layers:null,r.dataProjection=we(n.dataProjection?n.dataProjection:"EPSG:4326"),r}return bv(e,t),e.prototype.readFeaturesFromObject=function(t,e){if("Topology"==t.type){var r=t,n=void 0,i=null,o=null;r.transform&&(i=(n=r.transform).scale,o=n.translate);var a=r.arcs;n&&function(t,e,r){for(var n=0,i=t.length;n0&&i.pop(),n=r>=0?e[r]:e[~r].slice().reverse(),i.push.apply(i,n);for(var s=0,u=i.length;s=2,57),n}return Uv(e,t),e}(kv),Yv=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),zv=function(t){function e(e){return t.call(this,"And",Array.prototype.slice.call(arguments))||this}return Yv(e,t),e}(Bv),Xv=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Vv=function(t){function e(e,r,n){var i=t.call(this,"BBOX")||this;if(i.geometryName=e,i.extent=r,4!==r.length)throw new Error("Expected an extent with four values ([minX, minY, maxX, maxY])");return i.srsName=n,i}return Xv(e,t),e}(kv),Wv=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Zv=function(t){function e(e,r,n,i){var o=t.call(this,e)||this;return o.geometryName=r||"the_geom",o.geometry=n,o.srsName=i,o}return Wv(e,t),e}(kv),Kv=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Hv=function(t){function e(e,r,n){return t.call(this,"Contains",e,r,n)||this}return Kv(e,t),e}(Zv),qv=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Jv=function(t){function e(e,r){var n=t.call(this,e)||this;return n.propertyName=r,n}return qv(e,t),e}(kv),Qv=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),$v=function(t){function e(e,r,n){var i=t.call(this,"During",e)||this;return i.begin=r,i.end=n,i}return Qv(e,t),e}(Jv),tm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),em=function(t){function e(e,r,n,i){var o=t.call(this,e,r)||this;return o.expression=n,o.matchCase=i,o}return tm(e,t),e}(Jv),rm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),nm=function(t){function e(e,r,n){return t.call(this,"PropertyIsEqualTo",e,r,n)||this}return rm(e,t),e}(em),im=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),om=function(t){function e(e,r){return t.call(this,"PropertyIsGreaterThan",e,r)||this}return im(e,t),e}(em),am=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),sm=function(t){function e(e,r){return t.call(this,"PropertyIsGreaterThanOrEqualTo",e,r)||this}return am(e,t),e}(em),um=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),lm=function(t){function e(e,r,n){return t.call(this,"Intersects",e,r,n)||this}return um(e,t),e}(Zv),hm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),cm=function(t){function e(e,r,n){var i=t.call(this,"PropertyIsBetween",e)||this;return i.lowerBoundary=r,i.upperBoundary=n,i}return hm(e,t),e}(Jv),pm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),fm=function(t){function e(e,r,n,i,o,a){var s=t.call(this,"PropertyIsLike",e)||this;return s.pattern=r,s.wildCard=void 0!==n?n:"*",s.singleChar=void 0!==i?i:".",s.escapeChar=void 0!==o?o:"!",s.matchCase=a,s}return pm(e,t),e}(Jv),dm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),_m=function(t){function e(e){return t.call(this,"PropertyIsNull",e)||this}return dm(e,t),e}(Jv),gm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),ym=function(t){function e(e,r){return t.call(this,"PropertyIsLessThan",e,r)||this}return gm(e,t),e}(em),vm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),mm=function(t){function e(e,r){return t.call(this,"PropertyIsLessThanOrEqualTo",e,r)||this}return vm(e,t),e}(em),Em=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Tm=function(t){function e(e){var r=t.call(this,"Not")||this;return r.condition=e,r}return Em(e,t),e}(kv),Sm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),wm=function(t){function e(e,r,n){return t.call(this,"PropertyIsNotEqualTo",e,r,n)||this}return Sm(e,t),e}(em),xm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Om=function(t){function e(e){return t.call(this,"Or",Array.prototype.slice.call(arguments))||this}return xm(e,t),e}(Bv),Rm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Cm=function(t){function e(e,r,n){return t.call(this,"Within",e,r,n)||this}return Rm(e,t),e}(Zv);function Pm(t){var e=[null].concat(Array.prototype.slice.call(arguments));return new(Function.prototype.bind.apply(zv,e))}function Im(t,e,r){return new Vv(t,e,r)}var bm=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),Lm={"http://www.opengis.net/gml":{boundedBy:js(Ad.prototype.readGeometryElement,"bounds")}},Mm={"http://www.opengis.net/wfs":{totalInserted:js(Ud),totalUpdated:js(Ud),totalDeleted:js(Ud)}},Fm={"http://www.opengis.net/wfs":{TransactionSummary:js(function(t,e){return Ws({},Mm,t,e)},"transactionSummary"),InsertResults:js(function(t,e){return Ws([],Vm,t,e)},"insertIds")}},Am={"http://www.opengis.net/wfs":{PropertyName:ks(Wd)}},Nm={"http://www.opengis.net/wfs":{Insert:ks(function(t,e,r){var n=r[r.length-1],i=n.featureType,o=n.featureNS,a=n.gmlVersion,s=bs(o,i);t.appendChild(s),2===a?i_.prototype.writeFeatureElement(s,e,r):Jd.prototype.writeFeatureElement(s,e,r)}),Update:ks(function(t,e,r){var n=r[r.length-1];K(void 0!==e.getId(),27);var i=n.featureType,o=n.featurePrefix,a=n.featureNS,s=Zm(o,i),u=e.getGeometryName();t.setAttribute("typeName",s),t.setAttributeNS(Gm,"xmlns:"+o,a);var l=e.getId();if(void 0!==l){for(var h=e.getKeys(),c=[],p=0,f=h.length;p="a"&&t<="z"||t>="A"&&t<="Z"},t.prototype.isNumeric_=function(t,e){return t>="0"&&t<="9"||"."==t&&!(void 0!==e&&e)},t.prototype.isWhiteSpace_=function(t){return" "==t||"\t"==t||"\r"==t||"\n"==t},t.prototype.nextChar_=function(){return this.wkt.charAt(++this.index_)},t.prototype.nextToken=function(){var t,e=this.nextChar_(),r=this.index_,n=e;if("("==e)t=lE;else if(","==e)t=pE;else if(")"==e)t=hE;else if(this.isNumeric_(e)||"-"==e)t=cE,n=this.readNumber_();else if(this.isAlpha_(e))t=uE,n=this.readText_();else{if(this.isWhiteSpace_(e))return this.nextToken();if(""!==e)throw new Error("Unexpected character: "+e);t=fE}return{position:r,value:n,type:t}},t.prototype.readNumber_=function(){var t,e=this.index_,r=!1,n=!1;do{"."==t?r=!0:"e"!=t&&"E"!=t||(n=!0),t=this.nextChar_()}while(this.isNumeric_(t,r)||!n&&("e"==t||"E"==t)||n&&("-"==t||"+"==t));return parseFloat(this.wkt.substring(e,this.index_--))},t.prototype.readText_=function(){var t,e=this.index_;do{t=this.nextChar_()}while(this.isAlpha_(t));return this.wkt.substring(e,this.index_--).toUpperCase()},t}(),yE=function(){function t(t){this.lexer_=t,this.token_,this.layout_=At.XY}return t.prototype.consume_=function(){this.token_=this.lexer_.nextToken()},t.prototype.isTokenType=function(t){return this.token_.type==t},t.prototype.match=function(t){var e=this.isTokenType(t);return e&&this.consume_(),e},t.prototype.parse=function(){return this.consume_(),this.parseGeometry_()},t.prototype.parseGeometryLayout_=function(){var t=At.XY,e=this.token_;if(this.isTokenType(uE)){var r=e.value;r===aE?t=At.XYZ:r===sE?t=At.XYM:"ZM"===r&&(t=At.XYZM),t!==At.XY&&this.consume_()}return t},t.prototype.parseGeometryCollectionText_=function(){if(this.match(lE)){var t=[];do{t.push(this.parseGeometry_())}while(this.match(pE));if(this.match(hE))return t}else if(this.isEmptyGeometry_())return[];throw new Error(this.formatErrorMessage_())},t.prototype.parsePointText_=function(){if(this.match(lE)){var t=this.parsePoint_();if(this.match(hE))return t}else if(this.isEmptyGeometry_())return null;throw new Error(this.formatErrorMessage_())},t.prototype.parseLineStringText_=function(){if(this.match(lE)){var t=this.parsePointList_();if(this.match(hE))return t}else if(this.isEmptyGeometry_())return[];throw new Error(this.formatErrorMessage_())},t.prototype.parsePolygonText_=function(){if(this.match(lE)){var t=this.parseLineStringTextList_();if(this.match(hE))return t}else if(this.isEmptyGeometry_())return[];throw new Error(this.formatErrorMessage_())},t.prototype.parseMultiPointText_=function(){if(this.match(lE)){var t=void 0;if(t=this.token_.type==lE?this.parsePointTextList_():this.parsePointList_(),this.match(hE))return t}else if(this.isEmptyGeometry_())return[];throw new Error(this.formatErrorMessage_())},t.prototype.parseMultiLineStringText_=function(){if(this.match(lE)){var t=this.parseLineStringTextList_();if(this.match(hE))return t}else if(this.isEmptyGeometry_())return[];throw new Error(this.formatErrorMessage_())},t.prototype.parseMultiPolygonText_=function(){if(this.match(lE)){var t=this.parsePolygonTextList_();if(this.match(hE))return t}else if(this.isEmptyGeometry_())return[];throw new Error(this.formatErrorMessage_())},t.prototype.parsePoint_=function(){for(var t=[],e=this.layout_.length,r=0;r0&&(e+=" "+n)}return 0===r.length?e+" "+oE:e+"("+r+")"}var xE=vE,OE="http://www.w3.org/1999/xlink";function RE(t){return t.getAttributeNS(OE,"href")}var CE=function(){function t(){}return t.prototype.read=function(t){if(t){if("string"==typeof t){var e=Fs(t);return this.readFromDocument(e)}return Ms(t)?this.readFromDocument(t):this.readFromNode(t)}return null},t.prototype.readFromDocument=function(t){},t.prototype.readFromNode=function(t){},t}(),PE=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),IE=[null,"http://www.opengis.net/wms"],bE=Xs(IE,{Service:js(function(t,e){return Ws({},FE,t,e)}),Capability:js(function(t,e){return Ws({},LE,t,e)})}),LE=Xs(IE,{Request:js(function(t,e){return Ws({},BE,t,e)}),Exception:js(function(t,e){return Ws([],GE,t,e)}),Layer:js(function(t,e){return Ws({},jE,t,e)})}),ME=function(t){function e(){var e=t.call(this)||this;return e.version=void 0,e}return PE(e,t),e.prototype.readFromDocument=function(t){for(var e=t.firstChild;e;e=e.nextSibling)if(e.nodeType==Node.ELEMENT_NODE)return this.readFromNode(e);return null},e.prototype.readFromNode=function(t){this.version=t.getAttribute("version").trim();var e=Ws({version:this.version},bE,t,[]);return e||null},e}(CE),FE=Xs(IE,{Name:js(Yd),Title:js(Yd),Abstract:js(Yd),KeywordList:js(JE),OnlineResource:js(RE),ContactInformation:js(function(t,e){return Ws({},AE,t,e)}),Fees:js(Yd),AccessConstraints:js(Yd),LayerLimit:js(Ud),MaxWidth:js(Ud),MaxHeight:js(Ud)}),AE=Xs(IE,{ContactPersonPrimary:js(function(t,e){return Ws({},NE,t,e)}),ContactPosition:js(Yd),ContactAddress:js(function(t,e){return Ws({},DE,t,e)}),ContactVoiceTelephone:js(Yd),ContactFacsimileTelephone:js(Yd),ContactElectronicMailAddress:js(Yd)}),NE=Xs(IE,{ContactPerson:js(Yd),ContactOrganization:js(Yd)}),DE=Xs(IE,{AddressType:js(Yd),Address:js(Yd),City:js(Yd),StateOrProvince:js(Yd),PostCode:js(Yd),Country:js(Yd)}),GE=Xs(IE,{Format:Ns(Yd)}),jE=Xs(IE,{Name:js(Yd),Title:js(Yd),Abstract:js(Yd),KeywordList:js(JE),CRS:Gs(Yd),EX_GeographicBoundingBox:js(function(t,e){var r=Ws({},UE,t,e);if(!r)return;var n=r.westBoundLongitude,i=r.southBoundLatitude,o=r.eastBoundLongitude,a=r.northBoundLatitude;if(void 0===n||void 0===i||void 0===o||void 0===a)return;return[n,i,o,a]}),BoundingBox:Gs(function(t,e){var r=[kd(t.getAttribute("minx")),kd(t.getAttribute("miny")),kd(t.getAttribute("maxx")),kd(t.getAttribute("maxy"))],n=[kd(t.getAttribute("resx")),kd(t.getAttribute("resy"))];return{crs:t.getAttribute("CRS"),extent:r,res:n}}),Dimension:Gs(function(t,e){return{name:t.getAttribute("name"),units:t.getAttribute("units"),unitSymbol:t.getAttribute("unitSymbol"),default:t.getAttribute("default"),multipleValues:Dd(t.getAttribute("multipleValues")),nearestValue:Dd(t.getAttribute("nearestValue")),current:Dd(t.getAttribute("current")),values:Yd(t)}}),Attribution:js(function(t,e){return Ws({},kE,t,e)}),AuthorityURL:Gs(function(t,e){var r=KE(t,e);if(r)return r.name=t.getAttribute("name"),r;return}),Identifier:Gs(Yd),MetadataURL:Gs(function(t,e){var r=KE(t,e);if(r)return r.type=t.getAttribute("type"),r;return}),DataURL:Gs(KE),FeatureListURL:Gs(KE),Style:Gs(function(t,e){return Ws({},VE,t,e)}),MinScaleDenominator:js(jd),MaxScaleDenominator:js(jd),Layer:Gs(function(t,e){var r=e[e.length-1],n=Ws({},jE,t,e);if(!n)return;var i=Dd(t.getAttribute("queryable"));void 0===i&&(i=r.queryable);n.queryable=void 0!==i&&i;var o=Bd(t.getAttribute("cascaded"));void 0===o&&(o=r.cascaded);n.cascaded=o;var a=Dd(t.getAttribute("opaque"));void 0===a&&(a=r.opaque);n.opaque=void 0!==a&&a;var s=Dd(t.getAttribute("noSubsets"));void 0===s&&(s=r.noSubsets);n.noSubsets=void 0!==s&&s;var u=kd(t.getAttribute("fixedWidth"));u||(u=r.fixedWidth);n.fixedWidth=u;var l=kd(t.getAttribute("fixedHeight"));l||(l=r.fixedHeight);n.fixedHeight=l,["Style","CRS","AuthorityURL"].forEach(function(t){if(t in r){var e=n[t]||[];n[t]=e.concat(r[t])}});return["EX_GeographicBoundingBox","BoundingBox","Dimension","Attribution","MinScaleDenominator","MaxScaleDenominator"].forEach(function(t){if(!(t in n)){var e=r[t];n[t]=e}}),n})}),kE=Xs(IE,{Title:js(Yd),OnlineResource:js(RE),LogoURL:js(qE)}),UE=Xs(IE,{westBoundLongitude:js(jd),eastBoundLongitude:js(jd),southBoundLatitude:js(jd),northBoundLatitude:js(jd)}),BE=Xs(IE,{GetCapabilities:js(HE),GetMap:js(HE),GetFeatureInfo:js(HE)}),YE=Xs(IE,{Format:Gs(Yd),DCPType:Gs(function(t,e){return Ws({},zE,t,e)})}),zE=Xs(IE,{HTTP:js(function(t,e){return Ws({},XE,t,e)})}),XE=Xs(IE,{Get:js(KE),Post:js(KE)}),VE=Xs(IE,{Name:js(Yd),Title:js(Yd),Abstract:js(Yd),LegendURL:Gs(qE),StyleSheetURL:js(KE),StyleURL:js(KE)}),WE=Xs(IE,{Format:js(Yd),OnlineResource:js(RE)}),ZE=Xs(IE,{Keyword:Ns(Yd)});function KE(t,e){return Ws({},WE,t,e)}function HE(t,e){return Ws({},YE,t,e)}function qE(t,e){var r=KE(t,e);if(r){var n=[Bd(t.getAttribute("width")),Bd(t.getAttribute("height"))];return r.size=n,r}}function JE(t,e){return Ws([],ZE,t,e)}var QE=ME,$E=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),tT=function(t){function e(e){var r=t.call(this)||this,n=e||{};return r.featureNS_="http://mapserver.gis.umn.edu/mapserver",r.gmlFormat_=new i_,r.layers_=n.layers?n.layers:null,r}return $E(e,t),e.prototype.getLayers=function(){return this.layers_},e.prototype.setLayers=function(t){this.layers_=t},e.prototype.readFeatures_=function(t,e){t.setAttribute("namespaceURI",this.featureNS_);var r=t.localName,n=[];if(0===t.childNodes.length)return n;if("msGMLOutput"==r)for(var i=0,o=t.childNodes.length;i.75*h||l>.75*c?this.resetExtent_():it(o,n)||this.recenter_()}}},e.prototype.resetExtent_=function(){var t=this.getMap(),e=this.ovmap_,r=t.getSize(),n=t.getView().calculateExtentInternal(r),i=e.getView(),o=Math.log(7.5)/Math.LN2;Mt(n,1/(.1*Math.pow(2,o/2))),i.fitInternal(zr(n))},e.prototype.recenter_=function(){var t=this.getMap(),e=this.ovmap_,r=t.getView();e.getView().setCenterInternal(r.getCenterInternal())},e.prototype.updateBox_=function(){var t=this.getMap(),e=this.ovmap_;if(t.isRendered()&&e.isRendered()){var r=t.getSize(),n=t.getView(),i=e.getView(),o=this.rotateWithView_?0:-n.getRotation(),a=this.boxOverlay_,s=this.boxOverlay_.getElement(),u=n.getCenterInternal(),l=n.getResolution(),h=i.getResolution(),c=r[0]*l/h,p=r[1]*l/h;if(a.setPosition(u),s){s.style.width=c+"px",s.style.height=p+"px";var f="rotate("+o+"rad)";s.style.transform=f}}},e.prototype.handleClick_=function(t){t.preventDefault(),this.handleToggle_()},e.prototype.handleToggle_=function(){this.element.classList.toggle(Gi),this.collapsed_?pi(this.collapseLabel_,this.label_):pi(this.label_,this.collapseLabel_),this.collapsed_=!this.collapsed_;var t=this.ovmap_;if(!this.collapsed_){if(t.isRendered())return this.viewExtent_=void 0,void t.render();t.updateSize(),this.resetExtent_(),y(t,dn,function(t){this.updateBox_()},this)}},e.prototype.getCollapsible=function(){return this.collapsible_},e.prototype.setCollapsible=function(t){this.collapsible_!==t&&(this.collapsible_=t,this.element.classList.toggle("ol-uncollapsible"),!t&&this.collapsed_&&this.handleToggle_())},e.prototype.setCollapsed=function(t){this.collapsible_&&this.collapsed_!==t&&this.handleToggle_()},e.prototype.getCollapsed=function(){return this.collapsed_},e.prototype.getRotateWithView=function(){return this.rotateWithView_},e.prototype.setRotateWithView=function(t){this.rotateWithView_!==t&&(this.rotateWithView_=t,0!==this.getMap().getView().getRotation()&&(this.rotateWithView_?this.handleRotationChanged_():this.ovmap_.getView().setRotation(0),this.viewExtent_=void 0,this.validateExtent_(),this.updateBox_()))},e.prototype.getOverviewMap=function(){return this.ovmap_},e}(Li),qT=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),JT="units",QT={DEGREES:"degrees",IMPERIAL:"imperial",NAUTICAL:"nautical",METRIC:"metric",US:"us"},$T=[1,2,5];function tS(t){var e=t.frameState;this.viewState_=e?e.viewState:null,this.updateElement_()}var eS=function(t){function e(e){var r=this,n=e||{},i=void 0!==n.className?n.className:n.bar?"ol-scale-bar":"ol-scale-line";return(r=t.call(this,{element:document.createElement("div"),render:n.render||tS,target:n.target})||this).innerElement_=document.createElement("div"),r.innerElement_.className=i+"-inner",r.element.className=i+" "+Ai,r.element.appendChild(r.innerElement_),r.viewState_=null,r.minWidth_=void 0!==n.minWidth?n.minWidth:64,r.renderedVisible_=!1,r.renderedWidth_=void 0,r.renderedHTML_="",r.addEventListener(Y(JT),r.handleUnitsChanged_),r.setUnits(n.units||QT.METRIC),r.scaleBar_=n.bar||!1,r.scaleBarSteps_=n.steps||4,r.scaleBarText_=n.text||!1,r}return qT(e,t),e.prototype.getUnits=function(){return this.get(JT)},e.prototype.handleUnitsChanged_=function(){this.updateElement_()},e.prototype.setUnits=function(t){this.set(JT,t)},e.prototype.updateElement_=function(){var t=this.viewState_;if(t){var e=t.center,r=t.projection,n=this.getUnits(),i=n==QT.DEGREES?te.DEGREES:te.METERS,o=xe(r,t.resolution,e,i),a=this.minWidth_*o,s="";if(n==QT.DEGREES){var u=$t[te.DEGREES];(a*=u)=this.minWidth_)break;++f}p=this.scaleBar_?this.createScaleBar(h,l,s):l.toFixed(c<0?-c:0)+" "+s,this.renderedHTML_!=p&&(this.innerElement_.innerHTML=p,this.renderedHTML_=p),this.renderedWidth_!=h&&(this.innerElement_.style.width=h+"px",this.renderedWidth_=h),this.renderedVisible_||(this.element.style.display="",this.renderedVisible_=!0)}else this.renderedVisible_&&(this.element.style.display="none",this.renderedVisible_=!1)},e.prototype.createScaleBar=function(t,e,r){for(var n="1 : "+Math.round(this.getScaleForResolution()).toLocaleString(),i=[],o=t/this.scaleBarSteps_,a="#ffffff",s=0;s
'+this.createMarker("relative",s)+(s%2==0||2===this.scaleBarSteps_?this.createStepText(s,t,!1,e,r):"")+""),s===this.scaleBarSteps_-1&&i.push(this.createStepText(s+1,t,!0,e,r)),a="#ffffff"===a?"#000000":"#ffffff";return'
'+(this.scaleBarText_?'
'+n+"
":"")+i.join("")+"
"},e.prototype.createMarker=function(t,e){return'
'},e.prototype.createStepText=function(t,e,r,n,i){var o=(0===t?0:Math.round(n/this.scaleBarSteps_*t*100)/100)+(0===t?"":" "+i);return'
'+o+"
"},e.prototype.getScaleForResolution=function(){var t=this.getMap().getView().getResolution(),e=this.viewState_.projection.getMetersPerUnit();return parseFloat(t.toString())*e*39.37*(25.4/.28)},e}(Li),rS=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),nS={VERTICAL:0,HORIZONTAL:1};function iS(t){if(t.frameState){this.sliderInitialized_||this.initSlider_();var e=t.frameState.viewState.resolution;this.currentResolution_=e,this.setThumbPosition_(e)}}var oS=function(t){function e(e){var r=this,n=e||{};(r=t.call(this,{element:document.createElement("div"),render:n.render||iS})||this).dragListenerKeys_=[],r.currentResolution_=void 0,r.direction_=nS.VERTICAL,r.dragging_,r.heightLimit_=0,r.widthLimit_=0,r.startX_,r.startY_,r.thumbSize_=null,r.sliderInitialized_=!1,r.duration_=void 0!==n.duration?n.duration:200;var i=void 0!==n.className?n.className:"ol-zoomslider",o=document.createElement("button");o.setAttribute("type","button"),o.className=i+"-thumb "+Ai;var a=r.element;return a.setAttribute("touch-action","none"),a.className=i+" "+Ai+" "+Di,a.appendChild(o),a.addEventListener(cn.POINTERDOWN,r.handleDraggerStart_.bind(r),!1),a.addEventListener(cn.POINTERMOVE,r.handleDraggerDrag_.bind(r),!1),a.addEventListener(cn.POINTERUP,r.handleDraggerEnd_.bind(r),!1),a.addEventListener(N.CLICK,r.handleContainerClick_.bind(r),!1),o.addEventListener(N.CLICK,L,!1),r}return rS(e,t),e.prototype.setMap=function(e){t.prototype.setMap.call(this,e),e&&e.render()},e.prototype.initSlider_=function(){var t=this.element,e=t.offsetWidth,r=t.offsetHeight,n=t.firstElementChild,i=getComputedStyle(n),o=n.offsetWidth+parseFloat(i.marginRight)+parseFloat(i.marginLeft),a=n.offsetHeight+parseFloat(i.marginTop)+parseFloat(i.marginBottom);this.thumbSize_=[o,a],e>r?(this.direction_=nS.HORIZONTAL,this.widthLimit_=e-o):(this.direction_=nS.VERTICAL,this.heightLimit_=r-a),this.sliderInitialized_=!0},e.prototype.handleContainerClick_=function(t){var e=this.getMap().getView(),r=this.getRelativePosition_(t.offsetX-this.thumbSize_[0]/2,t.offsetY-this.thumbSize_[1]/2),n=this.getResolutionForPosition_(r),i=e.getConstrainedZoom(e.getZoomForResolution(n));e.animateInternal({zoom:i,duration:this.duration_,easing:ei})},e.prototype.handleDraggerStart_=function(t){if(!this.dragging_&&t.target===this.element.firstElementChild){var e=this.element.firstElementChild;if(this.getMap().getView().beginInteraction(),this.startX_=t.clientX-parseFloat(e.style.left),this.startY_=t.clientY-parseFloat(e.style.top),this.dragging_=!0,0===this.dragListenerKeys_.length){var r=this.handleDraggerDrag_,n=this.handleDraggerEnd_;this.dragListenerKeys_.push(g(document,cn.POINTERMOVE,r,this),g(document,cn.POINTERUP,n,this))}}},e.prototype.handleDraggerDrag_=function(t){if(this.dragging_){var e=t.clientX-this.startX_,r=t.clientY-this.startY_,n=this.getRelativePosition_(e,r);this.currentResolution_=this.getResolutionForPosition_(n),this.getMap().getView().setResolution(this.currentResolution_)}},e.prototype.handleDraggerEnd_=function(t){this.dragging_&&(this.getMap().getView().endInteraction(),this.dragging_=!1,this.startX_=void 0,this.startY_=void 0,this.dragListenerKeys_.forEach(v),this.dragListenerKeys_.length=0)},e.prototype.setThumbPosition_=function(t){var e=this.getPositionForResolution_(t),r=this.element.firstElementChild;this.direction_==nS.HORIZONTAL?r.style.left=this.widthLimit_*e+"px":r.style.top=this.heightLimit_*e+"px"},e.prototype.getRelativePosition_=function(t,e){return kt(this.direction_===nS.HORIZONTAL?t/this.widthLimit_:e/this.heightLimit_,0,1)},e.prototype.getResolutionForPosition_=function(t){return this.getMap().getView().getResolutionForValueFunction()(1-t)},e.prototype.getPositionForResolution_=function(t){return kt(1-this.getMap().getView().getValueForResolutionFunction()(t),0,1)},e}(Li),aS=function(){var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}}(),sS=function(t){function e(e){var r=this,n=e||{};(r=t.call(this,{element:document.createElement("div"),target:n.target})||this).extent=n.extent?n.extent:null;var i=void 0!==n.className?n.className:"ol-zoom-extent",o=void 0!==n.label?n.label:"E",a=void 0!==n.tipLabel?n.tipLabel:"Fit to extent",s=document.createElement("button");s.setAttribute("type","button"),s.title=a,s.appendChild("string"==typeof o?document.createTextNode(o):o),s.addEventListener(N.CLICK,r.handleClick_.bind(r),!1);var u=i+" "+Ai+" "+Di,l=r.element;return l.className=u,l.appendChild(s),r}return aS(e,t),e.prototype.handleClick_=function(t){t.preventDefault(),this.handleZoomToExtent()},e.prototype.handleZoomToExtent=function(){var t=this.getMap().getView(),e=this.extent?this.extent:t.getProjection().getExtent();t.fitInternal(zr(e))},e}(Li),uS={array:{},color:{},colorlike:{},control:{},coordinate:{},easing:{},events:{}};uS.events.condition={},uS.extent={},uS.featureloader={},uS.format={},uS.format.filter={},uS.geom={},uS.has={},uS.interaction={},uS.layer={},uS.loadingstrategy={},uS.proj={},uS.proj.Units={},uS.proj.proj4={},uS.render={},uS.render.canvas={},uS.renderer={},uS.renderer.canvas={},uS.renderer.webgl={},uS.size={},uS.source={},uS.sphere={},uS.style={},uS.style.IconImageCache={},uS.tilegrid={},uS.util={},uS.webgl={},uS.xml={},uS.Collection=Z,uS.Feature=q,uS.Geolocation=Hr,uS.Kinetic=qr,uS.Map=Ja,uS.Object=z,uS.Observable=G,uS.Observable.unByKey=function(t){if(Array.isArray(t))for(var e=0,r=t.length;e180)&&(r[0]=Vt(n+180,360)-180),r},uS.proj.transform=Me,uS.proj.transformExtent=Fe,uS.render.VectorContext=fa,uS.render.canvas.labelCache=Ea,uS.render.getRenderPixel=function(t,e){var r=e.slice(0);return Ze(t.inversePixelTransform.slice(),r),r},uS.render.getVectorContext=function(t){var e,r=t.frameState,n=Ve(t.inversePixelTransform.slice(),r.coordinateToPixelTransform),i=Ba(r.viewState.resolution,r.pixelRatio),o=je();return o&&(e=be(o,r.viewState.projection)),new Na(t.context,r.pixelRatio,r.extent,n,r.viewState.rotation,i,e)},uS.render.toContext=function(t,e){var r=t.canvas,n=e||{},i=n.pixelRatio||an,o=n.size;o&&(r.width=o[0]*i,r.height=o[1]*i,r.style.width=o[0]+"px",r.style.height=o[1]+"px");var a=[0,0,r.width,r.height],s=Ke([1,0,0,1,0,0],i,i);return new Na(t,i,a,s,0)},uS.renderer.Composite=Ha,uS.renderer.canvas.ImageLayer=jh,uS.renderer.canvas.TileLayer=Zh,uS.renderer.canvas.VectorImageLayer=Ap,uS.renderer.canvas.VectorLayer=Mp,uS.renderer.canvas.VectorTileLayer=kp,uS.renderer.webgl.PointsLayer=Wc,uS.size.toSize=xi,uS.source.BingMaps=Ol,uS.source.CartoDB=Il,uS.source.Cluster=jl,uS.source.IIIF=Ql,uS.source.Image=sh,uS.source.ImageArcGISRest=hh,uS.source.ImageCanvas=dh,uS.source.ImageMapGuide=gh,uS.source.ImageStatic=vh,uS.source.ImageWMS=Rh,uS.source.OSM=Ih,uS.source.OSM.ATTRIBUTION=Ph,uS.source.Raster=oc,uS.source.Source=cl,uS.source.Stamen=hc,uS.source.Tile=_l,uS.source.TileArcGISRest=fc,uS.source.TileDebug=gc,uS.source.TileImage=wl,uS.source.TileJSON=vc,uS.source.TileWMS=Tc,uS.source.UTFGrid=xc,uS.source.Vector=Dl,uS.source.VectorTile=bc,uS.source.WMTS=Ac,uS.source.WMTS.optionsFromCapabilities=function(t,e){var r=O(t.Contents.Layer,function(t,r,n){return t.Identifier==e.layer});if(null===r)return null;var n,i=t.Contents.TileMatrixSet;(n=r.TileMatrixSetLink.length>1?C(r.TileMatrixSetLink,"projection"in e?function(t,r,n){var o=O(i,function(e){return e.Identifier==t.TileMatrixSet}).SupportedCRS,a=we(o.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"))||we(o),s=we(e.projection);return a&&s?Ie(a,s):o==e.projection}:function(t,r,n){return t.TileMatrixSet==e.matrixSet}):0)<0&&(n=0);var o=r.TileMatrixSetLink[n].TileMatrixSet,a=r.TileMatrixSetLink[n].TileMatrixSetLimits,s=r.Format[0];"format"in e&&(s=e.format),(n=C(r.Style,function(t,r,n){return"style"in e?t.Title==e.style:t.isDefault}))<0&&(n=0);var u=r.Style[n].Identifier,l={};"Dimension"in r&&r.Dimension.forEach(function(t,e,r){var n=t.Identifier,i=t.Default;void 0===i&&(i=t.Value[0]),l[n]=i});var h,c=O(t.Contents.TileMatrixSet,function(t,e,r){return t.Identifier==o}),p=c.SupportedCRS;if(p&&(h=we(p.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"))||we(p)),"projection"in e){var f=we(e.projection);f&&(h&&!Ie(f,h)||(h=f))}var d,_,g=r.WGS84BoundingBox;if(void 0!==g){var y=we("EPSG:4326").getExtent();_=g[0]==y[0]&&g[2]==y[2],d=Fe(g,"EPSG:4326",h);var v=h.getExtent();v&&(it(v,d)||(d=void 0))}var m=du(c,d,a),E=[],S=e.requestEncoding;if(S=void 0!==S?S:"","OperationsMetadata"in t&&"GetTile"in t.OperationsMetadata)for(var w=t.OperationsMetadata.GetTile.DCP.HTTP.Get,x=0,R=w.length;xe;){if(a-e>600){var o=a-e+1,s=r-e+1,l=Math.log(o),f=.5*Math.exp(2*l/3),u=.5*Math.sqrt(l*f*(o-f)/o)*(s-o/2<0?-1:1),m=Math.max(e,Math.floor(r-s*f/o+u)),c=Math.min(a,Math.floor(r+(o-s)*f/o+u));t(n,r,m,c,h)}var p=n[r],d=e,x=a;for(i(n,e,r),h(n[a],p)>0&&i(n,e,a);d0;)x--}0===h(n[e],p)?i(n,e,x):i(n,++x,a),x<=r&&(e=x+1),r<=x&&(a=x-1)}}(t,r,e||0,a||t.length-1,h||n)}function i(t,i,n){var r=t[i];t[i]=t[n],t[n]=r}function n(t,i){return ti?1:0}var r=function(t){void 0===t&&(t=9),this._maxEntries=Math.max(4,t),this._minEntries=Math.max(2,Math.ceil(.4*this._maxEntries)),this.clear()};function e(t,i,n){if(!n)return i.indexOf(t);for(var r=0;r=t.minX&&i.maxY>=t.minY}function p(t){return{children:t,height:1,leaf:!0,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0}}function d(i,n,r,e,a){for(var h=[n,r];h.length;)if(!((r=h.pop())-(n=h.pop())<=e)){var o=n+Math.ceil((r-n)/e/2)*e;t(i,o,n,r,a),h.push(n,o,o,r)}}return r.prototype.all=function(){return this._all(this.data,[])},r.prototype.search=function(t){var i=this.data,n=[];if(!c(t,i))return n;for(var r=this.toBBox,e=[];i;){for(var a=0;a=0&&e[i].children.length>this._maxEntries;)this._split(e,i),i--;this._adjustParentBBoxes(r,e,i)},r.prototype._split=function(t,i){var n=t[i],r=n.children.length,e=this._minEntries;this._chooseSplitAxis(n,e,r);var h=this._chooseSplitIndex(n,e,r),o=p(n.children.splice(h,n.children.length-h));o.height=n.height,o.leaf=n.leaf,a(n,this.toBBox),a(o,this.toBBox),i?t[i-1].children.push(o):this._splitRoot(n,o)},r.prototype._splitRoot=function(t,i){this.data=p([t,i]),this.data.height=t.height+1,this.data.leaf=!1,a(this.data,this.toBBox)},r.prototype._chooseSplitIndex=function(t,i,n){for(var r,e,a,o,s,l,u,m=1/0,c=1/0,p=i;p<=n-i;p++){var d=h(t,0,p,this.toBBox),x=h(t,p,n,this.toBBox),v=(e=d,a=x,o=void 0,s=void 0,l=void 0,u=void 0,o=Math.max(e.minX,a.minX),s=Math.max(e.minY,a.minY),l=Math.min(e.maxX,a.maxX),u=Math.min(e.maxY,a.maxY),Math.max(0,l-o)*Math.max(0,u-s)),M=f(d)+f(x);v=i;c--){var p=t.children[c];o(s,t.leaf?e(p):p),l+=u(s)}return l},r.prototype._adjustParentBBoxes=function(t,i,n){for(var r=n;r>=0;r--)o(i[r],t)},r.prototype._condense=function(t){for(var i=t.length-1,n=void 0;i>=0;i--)0===t[i].children.length?i>0?(n=t[i-1].children).splice(n.indexOf(t[i]),1):this.clear():a(t[i],this.toBBox)},r});\n","/*!\n * PEP v0.5.3 | https://github.com/jquery/PEP\n * Copyright jQuery Foundation and other contributors | http://jquery.org/license\n */\n\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (global.PointerEventsPolyfill = factory());\n}(this, function () { 'use strict';\n\n /**\n * This is the constructor for new PointerEvents.\n *\n * New Pointer Events must be given a type, and an optional dictionary of\n * initialization properties.\n *\n * Due to certain platform requirements, events returned from the constructor\n * identify as MouseEvents.\n *\n * @constructor\n * @param {String} inType The type of the event to create.\n * @param {Object} [inDict] An optional dictionary of initial event properties.\n * @return {Event} A new PointerEvent of type `inType`, initialized with properties from `inDict`.\n */\n var MOUSE_PROPS = [\n 'bubbles',\n 'cancelable',\n 'view',\n 'screenX',\n 'screenY',\n 'clientX',\n 'clientY',\n 'ctrlKey',\n 'altKey',\n 'shiftKey',\n 'metaKey',\n 'button',\n 'relatedTarget',\n 'pageX',\n 'pageY'\n ];\n\n var MOUSE_DEFAULTS = [\n false,\n false,\n null,\n 0,\n 0,\n 0,\n 0,\n false,\n false,\n false,\n false,\n 0,\n null,\n 0,\n 0\n ];\n\n function PointerEvent(inType, inDict) {\n inDict = inDict || Object.create(null);\n\n var e = document.createEvent('Event');\n e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false);\n\n // define inherited MouseEvent properties\n // skip bubbles and cancelable since they're set above in initEvent()\n for (var i = 2, p; i < MOUSE_PROPS.length; i++) {\n p = MOUSE_PROPS[i];\n e[p] = inDict[p] || MOUSE_DEFAULTS[i];\n }\n e.buttons = inDict.buttons || 0;\n\n // Spec requires that pointers without pressure specified use 0.5 for down\n // state and 0 for up state.\n var pressure = 0;\n\n if (inDict.pressure !== undefined && e.buttons) {\n pressure = inDict.pressure;\n } else {\n pressure = e.buttons ? 0.5 : 0;\n }\n\n // add x/y properties aliased to clientX/Y\n e.x = e.clientX;\n e.y = e.clientY;\n\n // define the properties of the PointerEvent interface\n e.pointerId = inDict.pointerId || 0;\n e.width = inDict.width || 1;\n e.height = inDict.height || 1;\n e.pressure = pressure;\n e.tiltX = inDict.tiltX || 0;\n e.tiltY = inDict.tiltY || 0;\n e.twist = inDict.twist || 0;\n e.tangentialPressure = inDict.tangentialPressure || 0;\n e.pointerType = inDict.pointerType || '';\n e.hwTimestamp = inDict.hwTimestamp || 0;\n e.isPrimary = inDict.isPrimary || false;\n e.detail = 0;\n return e;\n }\n\n /**\n * This module implements a map of pointer states\n */\n var USE_MAP = window.Map && window.Map.prototype.forEach;\n var PointerMap = USE_MAP ? Map : SparseArrayMap;\n\n function SparseArrayMap() {\n this.array = [];\n this.size = 0;\n }\n\n SparseArrayMap.prototype = {\n set: function(k, v) {\n if (v === undefined) {\n return this.delete(k);\n }\n if (!this.has(k)) {\n this.size++;\n }\n this.array[k] = v;\n },\n has: function(k) {\n return this.array[k] !== undefined;\n },\n delete: function(k) {\n if (this.has(k)) {\n delete this.array[k];\n this.size--;\n }\n },\n get: function(k) {\n return this.array[k];\n },\n clear: function() {\n this.array.length = 0;\n this.size = 0;\n },\n\n // return value, key, map\n forEach: function(callback, thisArg) {\n return this.array.forEach(function(v, k) {\n callback.call(thisArg, v, k, this);\n }, this);\n }\n };\n\n var CLONE_PROPS = [\n\n // MouseEvent\n 'bubbles',\n 'cancelable',\n 'view',\n 'detail',\n 'screenX',\n 'screenY',\n 'clientX',\n 'clientY',\n 'ctrlKey',\n 'altKey',\n 'shiftKey',\n 'metaKey',\n 'button',\n 'relatedTarget',\n\n // DOM Level 3\n 'buttons',\n\n // PointerEvent\n 'pointerId',\n 'width',\n 'height',\n 'pressure',\n 'tiltX',\n 'tiltY',\n 'pointerType',\n 'hwTimestamp',\n 'isPrimary',\n\n // event instance\n 'type',\n 'target',\n 'currentTarget',\n 'which',\n 'pageX',\n 'pageY',\n 'timeStamp'\n ];\n\n var CLONE_DEFAULTS = [\n\n // MouseEvent\n false,\n false,\n null,\n null,\n 0,\n 0,\n 0,\n 0,\n false,\n false,\n false,\n false,\n 0,\n null,\n\n // DOM Level 3\n 0,\n\n // PointerEvent\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n '',\n 0,\n false,\n\n // event instance\n '',\n null,\n null,\n 0,\n 0,\n 0,\n 0\n ];\n\n var BOUNDARY_EVENTS = {\n 'pointerover': 1,\n 'pointerout': 1,\n 'pointerenter': 1,\n 'pointerleave': 1\n };\n\n var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined');\n\n /**\n * This module is for normalizing events. Mouse and Touch events will be\n * collected here, and fire PointerEvents that have the same semantics, no\n * matter the source.\n * Events fired:\n * - pointerdown: a pointing is added\n * - pointerup: a pointer is removed\n * - pointermove: a pointer is moved\n * - pointerover: a pointer crosses into an element\n * - pointerout: a pointer leaves an element\n * - pointercancel: a pointer will no longer generate events\n */\n var dispatcher = {\n pointermap: new PointerMap(),\n eventMap: Object.create(null),\n captureInfo: Object.create(null),\n\n // Scope objects for native events.\n // This exists for ease of testing.\n eventSources: Object.create(null),\n eventSourceList: [],\n /**\n * Add a new event source that will generate pointer events.\n *\n * `inSource` must contain an array of event names named `events`, and\n * functions with the names specified in the `events` array.\n * @param {string} name A name for the event source\n * @param {Object} source A new source of platform events.\n */\n registerSource: function(name, source) {\n var s = source;\n var newEvents = s.events;\n if (newEvents) {\n newEvents.forEach(function(e) {\n if (s[e]) {\n this.eventMap[e] = s[e].bind(s);\n }\n }, this);\n this.eventSources[name] = s;\n this.eventSourceList.push(s);\n }\n },\n register: function(element) {\n var l = this.eventSourceList.length;\n for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {\n\n // call eventsource register\n es.register.call(es, element);\n }\n },\n unregister: function(element) {\n var l = this.eventSourceList.length;\n for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {\n\n // call eventsource register\n es.unregister.call(es, element);\n }\n },\n contains: /*scope.external.contains || */function(container, contained) {\n try {\n return container.contains(contained);\n } catch (ex) {\n\n // most likely: https://bugzilla.mozilla.org/show_bug.cgi?id=208427\n return false;\n }\n },\n\n // EVENTS\n down: function(inEvent) {\n inEvent.bubbles = true;\n this.fireEvent('pointerdown', inEvent);\n },\n move: function(inEvent) {\n inEvent.bubbles = true;\n this.fireEvent('pointermove', inEvent);\n },\n up: function(inEvent) {\n inEvent.bubbles = true;\n this.fireEvent('pointerup', inEvent);\n },\n enter: function(inEvent) {\n inEvent.bubbles = false;\n this.fireEvent('pointerenter', inEvent);\n },\n leave: function(inEvent) {\n inEvent.bubbles = false;\n this.fireEvent('pointerleave', inEvent);\n },\n over: function(inEvent) {\n inEvent.bubbles = true;\n this.fireEvent('pointerover', inEvent);\n },\n out: function(inEvent) {\n inEvent.bubbles = true;\n this.fireEvent('pointerout', inEvent);\n },\n cancel: function(inEvent) {\n inEvent.bubbles = true;\n this.fireEvent('pointercancel', inEvent);\n },\n leaveOut: function(event) {\n this.out(event);\n this.propagate(event, this.leave, false);\n },\n enterOver: function(event) {\n this.over(event);\n this.propagate(event, this.enter, true);\n },\n\n // LISTENER LOGIC\n eventHandler: function(inEvent) {\n\n // This is used to prevent multiple dispatch of pointerevents from\n // platform events. This can happen when two elements in different scopes\n // are set up to create pointer events, which is relevant to Shadow DOM.\n if (inEvent._handledByPE) {\n return;\n }\n var type = inEvent.type;\n var fn = this.eventMap && this.eventMap[type];\n if (fn) {\n fn(inEvent);\n }\n inEvent._handledByPE = true;\n },\n\n // set up event listeners\n listen: function(target, events) {\n events.forEach(function(e) {\n this.addEvent(target, e);\n }, this);\n },\n\n // remove event listeners\n unlisten: function(target, events) {\n events.forEach(function(e) {\n this.removeEvent(target, e);\n }, this);\n },\n addEvent: /*scope.external.addEvent || */function(target, eventName) {\n target.addEventListener(eventName, this.boundHandler);\n },\n removeEvent: /*scope.external.removeEvent || */function(target, eventName) {\n target.removeEventListener(eventName, this.boundHandler);\n },\n\n // EVENT CREATION AND TRACKING\n /**\n * Creates a new Event of type `inType`, based on the information in\n * `inEvent`.\n *\n * @param {string} inType A string representing the type of event to create\n * @param {Event} inEvent A platform event with a target\n * @return {Event} A PointerEvent of type `inType`\n */\n makeEvent: function(inType, inEvent) {\n\n // relatedTarget must be null if pointer is captured\n if (this.captureInfo[inEvent.pointerId]) {\n inEvent.relatedTarget = null;\n }\n var e = new PointerEvent(inType, inEvent);\n if (inEvent.preventDefault) {\n e.preventDefault = inEvent.preventDefault;\n }\n e._target = e._target || inEvent.target;\n return e;\n },\n\n // make and dispatch an event in one call\n fireEvent: function(inType, inEvent) {\n var e = this.makeEvent(inType, inEvent);\n return this.dispatchEvent(e);\n },\n /**\n * Returns a snapshot of inEvent, with writable properties.\n *\n * @param {Event} inEvent An event that contains properties to copy.\n * @return {Object} An object containing shallow copies of `inEvent`'s\n * properties.\n */\n cloneEvent: function(inEvent) {\n var eventCopy = Object.create(null);\n var p;\n for (var i = 0; i < CLONE_PROPS.length; i++) {\n p = CLONE_PROPS[i];\n eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];\n\n // Work around SVGInstanceElement shadow tree\n // Return the element that is represented by the instance for Safari, Chrome, IE.\n // This is the behavior implemented by Firefox.\n if (HAS_SVG_INSTANCE && (p === 'target' || p === 'relatedTarget')) {\n if (eventCopy[p] instanceof SVGElementInstance) {\n eventCopy[p] = eventCopy[p].correspondingUseElement;\n }\n }\n }\n\n // keep the semantics of preventDefault\n if (inEvent.preventDefault) {\n eventCopy.preventDefault = function() {\n inEvent.preventDefault();\n };\n }\n return eventCopy;\n },\n getTarget: function(inEvent) {\n var capture = this.captureInfo[inEvent.pointerId];\n if (!capture) {\n return inEvent._target;\n }\n if (inEvent._target === capture || !(inEvent.type in BOUNDARY_EVENTS)) {\n return capture;\n }\n },\n propagate: function(event, fn, propagateDown) {\n var target = event.target;\n var targets = [];\n\n // Order of conditions due to document.contains() missing in IE.\n while (target != null && target !== document && !target.contains(event.relatedTarget)) {\n targets.push(target);\n target = target.parentNode;\n\n // Touch: Do not propagate if node is detached.\n if (!target) {\n return;\n }\n }\n if (propagateDown) {\n targets.reverse();\n }\n targets.forEach(function(target) {\n event.target = target;\n fn.call(this, event);\n }, this);\n },\n setCapture: function(inPointerId, inTarget, skipDispatch) {\n if (this.captureInfo[inPointerId]) {\n this.releaseCapture(inPointerId, skipDispatch);\n }\n\n this.captureInfo[inPointerId] = inTarget;\n this.implicitRelease = this.releaseCapture.bind(this, inPointerId, skipDispatch);\n document.addEventListener('pointerup', this.implicitRelease);\n document.addEventListener('pointercancel', this.implicitRelease);\n\n var e = new PointerEvent('gotpointercapture', { bubbles: true });\n e.pointerId = inPointerId;\n e._target = inTarget;\n\n if (!skipDispatch) {\n this.asyncDispatchEvent(e);\n }\n },\n releaseCapture: function(inPointerId, skipDispatch) {\n var t = this.captureInfo[inPointerId];\n if (!t) {\n return;\n }\n\n this.captureInfo[inPointerId] = undefined;\n document.removeEventListener('pointerup', this.implicitRelease);\n document.removeEventListener('pointercancel', this.implicitRelease);\n\n var e = new PointerEvent('lostpointercapture', { bubbles: true });\n e.pointerId = inPointerId;\n e._target = t;\n\n if (!skipDispatch) {\n this.asyncDispatchEvent(e);\n }\n },\n /**\n * Dispatches the event to its target.\n *\n * @param {Event} inEvent The event to be dispatched.\n * @return {Boolean} True if an event handler returns true, false otherwise.\n */\n dispatchEvent: /*scope.external.dispatchEvent || */function(inEvent) {\n var t = this.getTarget(inEvent);\n if (t) {\n return t.dispatchEvent(inEvent);\n }\n },\n asyncDispatchEvent: function(inEvent) {\n requestAnimationFrame(this.dispatchEvent.bind(this, inEvent));\n }\n };\n dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);\n\n var targeting = {\n shadow: function(inEl) {\n if (inEl) {\n return inEl.shadowRoot || inEl.webkitShadowRoot;\n }\n },\n canTarget: function(shadow) {\n return shadow && Boolean(shadow.elementFromPoint);\n },\n targetingShadow: function(inEl) {\n var s = this.shadow(inEl);\n if (this.canTarget(s)) {\n return s;\n }\n },\n olderShadow: function(shadow) {\n var os = shadow.olderShadowRoot;\n if (!os) {\n var se = shadow.querySelector('shadow');\n if (se) {\n os = se.olderShadowRoot;\n }\n }\n return os;\n },\n allShadows: function(element) {\n var shadows = [];\n var s = this.shadow(element);\n while (s) {\n shadows.push(s);\n s = this.olderShadow(s);\n }\n return shadows;\n },\n searchRoot: function(inRoot, x, y) {\n if (inRoot) {\n var t = inRoot.elementFromPoint(x, y);\n var st, sr;\n\n // is element a shadow host?\n sr = this.targetingShadow(t);\n while (sr) {\n\n // find the the element inside the shadow root\n st = sr.elementFromPoint(x, y);\n if (!st) {\n\n // check for older shadows\n sr = this.olderShadow(sr);\n } else {\n\n // shadowed element may contain a shadow root\n var ssr = this.targetingShadow(st);\n return this.searchRoot(ssr, x, y) || st;\n }\n }\n\n // light dom element is the target\n return t;\n }\n },\n owner: function(element) {\n var s = element;\n\n // walk up until you hit the shadow root or document\n while (s.parentNode) {\n s = s.parentNode;\n }\n\n // the owner element is expected to be a Document or ShadowRoot\n if (s.nodeType !== Node.DOCUMENT_NODE && s.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {\n s = document;\n }\n return s;\n },\n findTarget: function(inEvent) {\n var x = inEvent.clientX;\n var y = inEvent.clientY;\n\n // if the listener is in the shadow root, it is much faster to start there\n var s = this.owner(inEvent.target);\n\n // if x, y is not in this root, fall back to document search\n if (!s.elementFromPoint(x, y)) {\n s = document;\n }\n return this.searchRoot(s, x, y);\n }\n };\n\n var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);\n var map = Array.prototype.map.call.bind(Array.prototype.map);\n var toArray = Array.prototype.slice.call.bind(Array.prototype.slice);\n var filter = Array.prototype.filter.call.bind(Array.prototype.filter);\n var MO = window.MutationObserver || window.WebKitMutationObserver;\n var SELECTOR = '[touch-action]';\n var OBSERVER_INIT = {\n subtree: true,\n childList: true,\n attributes: true,\n attributeOldValue: true,\n attributeFilter: ['touch-action']\n };\n\n function Installer(add, remove, changed, binder) {\n this.addCallback = add.bind(binder);\n this.removeCallback = remove.bind(binder);\n this.changedCallback = changed.bind(binder);\n if (MO) {\n this.observer = new MO(this.mutationWatcher.bind(this));\n }\n }\n\n Installer.prototype = {\n watchSubtree: function(target) {\n\n // Only watch scopes that can target find, as these are top-level.\n // Otherwise we can see duplicate additions and removals that add noise.\n //\n // TODO(dfreedman): For some instances with ShadowDOMPolyfill, we can see\n // a removal without an insertion when a node is redistributed among\n // shadows. Since it all ends up correct in the document, watching only\n // the document will yield the correct mutations to watch.\n if (this.observer && targeting.canTarget(target)) {\n this.observer.observe(target, OBSERVER_INIT);\n }\n },\n enableOnSubtree: function(target) {\n this.watchSubtree(target);\n if (target === document && document.readyState !== 'complete') {\n this.installOnLoad();\n } else {\n this.installNewSubtree(target);\n }\n },\n installNewSubtree: function(target) {\n forEach(this.findElements(target), this.addElement, this);\n },\n findElements: function(target) {\n if (target.querySelectorAll) {\n return target.querySelectorAll(SELECTOR);\n }\n return [];\n },\n removeElement: function(el) {\n this.removeCallback(el);\n },\n addElement: function(el) {\n this.addCallback(el);\n },\n elementChanged: function(el, oldValue) {\n this.changedCallback(el, oldValue);\n },\n concatLists: function(accum, list) {\n return accum.concat(toArray(list));\n },\n\n // register all touch-action = none nodes on document load\n installOnLoad: function() {\n document.addEventListener('readystatechange', function() {\n if (document.readyState === 'complete') {\n this.installNewSubtree(document);\n }\n }.bind(this));\n },\n isElement: function(n) {\n return n.nodeType === Node.ELEMENT_NODE;\n },\n flattenMutationTree: function(inNodes) {\n\n // find children with touch-action\n var tree = map(inNodes, this.findElements, this);\n\n // make sure the added nodes are accounted for\n tree.push(filter(inNodes, this.isElement));\n\n // flatten the list\n return tree.reduce(this.concatLists, []);\n },\n mutationWatcher: function(mutations) {\n mutations.forEach(this.mutationHandler, this);\n },\n mutationHandler: function(m) {\n if (m.type === 'childList') {\n var added = this.flattenMutationTree(m.addedNodes);\n added.forEach(this.addElement, this);\n var removed = this.flattenMutationTree(m.removedNodes);\n removed.forEach(this.removeElement, this);\n } else if (m.type === 'attributes') {\n this.elementChanged(m.target, m.oldValue);\n }\n }\n };\n\n function shadowSelector(s) {\n return 'body /shadow-deep/ ' + s;\n }\n function rule(v) {\n return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + '; }';\n }\n var attrib2css = [\n { selector: '[touch-action=\"none\"]', value: 'none' },\n { selector: '[touch-action=\"auto\"]', value: 'auto' },\n { selector: '[touch-action~=\"pan-x\"]', value: 'pan-x' },\n { selector: '[touch-action~=\"pan-y\"]', value: 'pan-y' },\n { selector: '[touch-action~=\"pan-up\"]', value: 'pan-up' },\n { selector: '[touch-action~=\"pan-down\"]', value: 'pan-down' },\n { selector: '[touch-action~=\"pan-left\"]', value: 'pan-left' },\n { selector: '[touch-action~=\"pan-right\"]', value: 'pan-right' }\n ];\n var styles = '';\n\n // only install stylesheet if the browser has touch action support\n var hasNativePE = window.PointerEvent || window.MSPointerEvent;\n\n // only add shadow selectors if shadowdom is supported\n var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoot;\n\n function applyAttributeStyles() {\n if (hasNativePE) {\n attrib2css.forEach(function(r) {\n styles += r.selector + rule(r.value) + '\\n';\n if (hasShadowRoot) {\n styles += shadowSelector(r.selector) + rule(r.value) + '\\n';\n }\n });\n\n var el = document.createElement('style');\n el.textContent = styles;\n document.head.appendChild(el);\n }\n }\n\n var pointermap = dispatcher.pointermap;\n\n // radius around touchend that swallows mouse events\n var DEDUP_DIST = 25;\n\n // left, middle, right, back, forward\n var BUTTON_TO_BUTTONS = [1, 4, 2, 8, 16];\n\n var HAS_BUTTONS = false;\n try {\n HAS_BUTTONS = new MouseEvent('test', { buttons: 1 }).buttons === 1;\n } catch (e) {}\n\n // handler block for native mouse events\n var mouseEvents = {\n POINTER_ID: 1,\n POINTER_TYPE: 'mouse',\n events: [\n 'mousedown',\n 'webkitmouseforcechanged',\n 'mousemove',\n 'mouseup',\n 'mouseover',\n 'mouseout'\n ],\n register: function(target) {\n dispatcher.listen(target, this.events);\n },\n unregister: function(target) {\n dispatcher.unlisten(target, this.events);\n },\n lastTouches: [],\n\n // collide with the global mouse listener\n isEventSimulatedFromTouch: function(inEvent) {\n var lts = this.lastTouches;\n var x = inEvent.clientX;\n var y = inEvent.clientY;\n for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {\n\n // simulated mouse events will be swallowed near a primary touchend\n var dx = Math.abs(x - t.x);\n var dy = Math.abs(y - t.y);\n if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {\n return true;\n }\n }\n },\n prepareEvent: function(inEvent) {\n var e = dispatcher.cloneEvent(inEvent);\n\n // forward mouse preventDefault\n var pd = e.preventDefault;\n e.preventDefault = function() {\n inEvent.preventDefault();\n pd();\n };\n e.pointerId = this.POINTER_ID;\n e.isPrimary = true;\n e.pointerType = this.POINTER_TYPE;\n if ('webkitForce' in inEvent) {\n e.pressure = inEvent.webkitForce - MouseEvent.WEBKIT_FORCE_AT_MOUSE_DOWN;\n }\n return e;\n },\n prepareButtonsForMove: function(e, inEvent) {\n var p = pointermap.get(this.POINTER_ID);\n\n // Update buttons state after possible out-of-document mouseup.\n if (inEvent.which === 0 || !p) {\n e.buttons = 0;\n } else {\n e.buttons = p.buttons;\n }\n inEvent.buttons = e.buttons;\n },\n mousedown: function(inEvent) {\n if (!this.isEventSimulatedFromTouch(inEvent)) {\n var p = pointermap.get(this.POINTER_ID);\n var e = this.prepareEvent(inEvent);\n if (!HAS_BUTTONS) {\n e.buttons = BUTTON_TO_BUTTONS[e.button];\n if (p) { e.buttons |= p.buttons; }\n inEvent.buttons = e.buttons;\n }\n pointermap.set(this.POINTER_ID, inEvent);\n if (!p || p.buttons === 0) {\n dispatcher.down(e);\n } else {\n dispatcher.move(e);\n }\n }\n },\n\n // This is called when the user force presses without moving x/y\n webkitmouseforcechanged: function(inEvent) {\n this.mousemove(inEvent);\n },\n mousemove: function(inEvent) {\n if (!this.isEventSimulatedFromTouch(inEvent)) {\n var e = this.prepareEvent(inEvent);\n if (!HAS_BUTTONS) { this.prepareButtonsForMove(e, inEvent); }\n e.button = -1;\n pointermap.set(this.POINTER_ID, inEvent);\n dispatcher.move(e);\n }\n },\n mouseup: function(inEvent) {\n if (!this.isEventSimulatedFromTouch(inEvent)) {\n var p = pointermap.get(this.POINTER_ID);\n var e = this.prepareEvent(inEvent);\n if (!HAS_BUTTONS) {\n var up = BUTTON_TO_BUTTONS[e.button];\n\n // Produces wrong state of buttons in Browsers without `buttons` support\n // when a mouse button that was pressed outside the document is released\n // inside and other buttons are still pressed down.\n e.buttons = p ? p.buttons & ~up : 0;\n inEvent.buttons = e.buttons;\n }\n pointermap.set(this.POINTER_ID, inEvent);\n\n // Support: Firefox <=44 only\n // FF Ubuntu includes the lifted button in the `buttons` property on\n // mouseup.\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1223366\n e.buttons &= ~BUTTON_TO_BUTTONS[e.button];\n if (e.buttons === 0) {\n dispatcher.up(e);\n } else {\n dispatcher.move(e);\n }\n }\n },\n mouseover: function(inEvent) {\n if (!this.isEventSimulatedFromTouch(inEvent)) {\n var e = this.prepareEvent(inEvent);\n if (!HAS_BUTTONS) { this.prepareButtonsForMove(e, inEvent); }\n e.button = -1;\n pointermap.set(this.POINTER_ID, inEvent);\n dispatcher.enterOver(e);\n }\n },\n mouseout: function(inEvent) {\n if (!this.isEventSimulatedFromTouch(inEvent)) {\n var e = this.prepareEvent(inEvent);\n if (!HAS_BUTTONS) { this.prepareButtonsForMove(e, inEvent); }\n e.button = -1;\n dispatcher.leaveOut(e);\n }\n },\n cancel: function(inEvent) {\n var e = this.prepareEvent(inEvent);\n dispatcher.cancel(e);\n this.deactivateMouse();\n },\n deactivateMouse: function() {\n pointermap.delete(this.POINTER_ID);\n }\n };\n\n var captureInfo = dispatcher.captureInfo;\n var findTarget = targeting.findTarget.bind(targeting);\n var allShadows = targeting.allShadows.bind(targeting);\n var pointermap$1 = dispatcher.pointermap;\n\n // this should be long enough to ignore compat mouse events made by touch\n var DEDUP_TIMEOUT = 2500;\n var ATTRIB = 'touch-action';\n var INSTALLER;\n\n // bitmask for _scrollType\n var UP = 1;\n var DOWN = 2;\n var LEFT = 4;\n var RIGHT = 8;\n var AUTO = UP | DOWN | LEFT | RIGHT;\n\n // handler block for native touch events\n var touchEvents = {\n events: [\n 'touchstart',\n 'touchmove',\n 'touchforcechange',\n 'touchend',\n 'touchcancel'\n ],\n register: function(target) {\n INSTALLER.enableOnSubtree(target);\n },\n unregister: function() {\n\n // TODO(dfreedman): is it worth it to disconnect the MO?\n },\n elementAdded: function(el) {\n var a = el.getAttribute(ATTRIB);\n var st = this.touchActionToScrollType(a);\n if (typeof st === \"number\") {\n el._scrollType = st;\n dispatcher.listen(el, this.events);\n\n // set touch-action on shadows as well\n allShadows(el).forEach(function(s) {\n s._scrollType = st;\n dispatcher.listen(s, this.events);\n }, this);\n }\n },\n elementRemoved: function(el) {\n\n // In some cases, an element is removed before a touchend.\n // When this is the case, we should wait for the touchend before unlistening,\n // because we still want pointer events to bubble up after removing from DOM.\n if (pointermap$1.size > 0) {\n var evts = this.events;\n el.addEventListener('touchend', function() {\n el._scrollType = undefined;\n dispatcher.unlisten(el, evts);\n });\n } else {\n el._scrollType = undefined;\n dispatcher.unlisten(el, this.events);\n }\n\n // remove touch-action from shadow\n allShadows(el).forEach(function(s) {\n s._scrollType = undefined;\n dispatcher.unlisten(s, this.events);\n }, this);\n },\n elementChanged: function(el, oldValue) {\n var a = el.getAttribute(ATTRIB);\n var st = this.touchActionToScrollType(a);\n var oldSt = this.touchActionToScrollType(oldValue);\n\n // simply update scrollType if listeners are already established\n if (typeof st === \"number\" && typeof oldSt === \"number\") {\n el._scrollType = st;\n allShadows(el).forEach(function(s) {\n s._scrollType = st;\n }, this);\n } else if (typeof oldSt === \"number\") {\n this.elementRemoved(el);\n } else if (typeof st === \"number\") {\n this.elementAdded(el);\n }\n },\n scrollTypes: {\n UP: function(s) {\n return s.includes('pan-y') || s.includes('pan-up') ? UP : 0;\n },\n DOWN: function(s) {\n return s.includes('pan-y') || s.includes('pan-down') ? DOWN : 0;\n },\n LEFT: function(s) {\n return s.includes('pan-x') || s.includes('pan-left') ? LEFT : 0;\n },\n RIGHT: function(s) {\n return s.includes('pan-x') || s.includes('pan-right') ? RIGHT : 0;\n }\n },\n touchActionToScrollType: function(touchAction) {\n if (!touchAction) {\n return;\n }\n\n if (touchAction === \"auto\") {\n return AUTO;\n }\n\n if (touchAction === \"none\") {\n return 0;\n }\n\n var s = touchAction.split(' ');\n var st = this.scrollTypes;\n\n // construct a bitmask of allowed scroll directions\n return st.UP(s) | st.DOWN(s) | st.LEFT(s) | st.RIGHT(s);\n },\n POINTER_TYPE: 'touch',\n firstTouch: null,\n isPrimaryTouch: function(inTouch) {\n return this.firstTouch === inTouch.identifier;\n },\n setPrimaryTouch: function(inTouch) {\n\n // set primary touch if there no pointers, or the only pointer is the mouse\n if (pointermap$1.size === 0 || (pointermap$1.size === 1 && pointermap$1.has(1))) {\n this.firstTouch = inTouch.identifier;\n this.firstXY = { X: inTouch.clientX, Y: inTouch.clientY };\n this.scrolling = false;\n }\n },\n removePrimaryPointer: function(inPointer) {\n if (inPointer.isPrimary) {\n this.firstTouch = null;\n this.firstXY = null;\n }\n },\n typeToButtons: function(type) {\n var ret = 0;\n if (type === 'touchstart' || type === 'touchmove' || type === 'touchforcechange') {\n ret = 1;\n }\n return ret;\n },\n touchToPointer: function(inTouch) {\n var cte = this.currentTouchEvent;\n var e = dispatcher.cloneEvent(inTouch);\n\n // We reserve pointerId 1 for Mouse.\n // Touch identifiers can start at 0.\n // Add 2 to the touch identifier for compatibility.\n var id = e.pointerId = inTouch.identifier + 2;\n e.target = captureInfo[id] || findTarget(e);\n e.bubbles = true;\n e.cancelable = true;\n e.button = 0;\n e.buttons = this.typeToButtons(cte.type);\n e.width = (inTouch.radiusX || inTouch.webkitRadiusX || 0) * 2;\n e.height = (inTouch.radiusY || inTouch.webkitRadiusY || 0) * 2;\n e.pressure = inTouch.force !== undefined ?\n inTouch.force :\n inTouch.webkitForce !== undefined ?\n inTouch.webkitForce : undefined;\n e.isPrimary = this.isPrimaryTouch(inTouch);\n if (inTouch.altitudeAngle) {\n var tan = Math.tan(inTouch.altitudeAngle);\n var radToDeg = 180 / Math.PI;\n e.tiltX = Math.atan(Math.cos(inTouch.azimuthAngle) / tan) * radToDeg;\n e.tiltY = Math.atan(Math.sin(inTouch.azimuthAngle) / tan) * radToDeg;\n } else {\n e.tiltX = 0;\n e.tiltY = 0;\n }\n if (inTouch.touchType === 'stylus') {\n e.pointerType = 'pen';\n } else {\n e.pointerType = this.POINTER_TYPE;\n }\n\n // forward modifier keys\n e.altKey = cte.altKey;\n e.ctrlKey = cte.ctrlKey;\n e.metaKey = cte.metaKey;\n e.shiftKey = cte.shiftKey;\n\n // forward touch preventDefaults\n var self = this;\n e.preventDefault = function() {\n self.scrolling = false;\n self.firstXY = null;\n cte.preventDefault();\n };\n return e;\n },\n processTouches: function(inEvent, inFunction) {\n var tl = inEvent.changedTouches;\n this.currentTouchEvent = inEvent;\n for (var i = 0, t; i < tl.length; i++) {\n t = tl[i];\n inFunction.call(this, this.touchToPointer(t));\n }\n },\n\n // For single axis scrollers, determines whether the element should emit\n // pointer events or behave as a scroller\n shouldScroll: function(inEvent) {\n if (this.firstXY) {\n var ret;\n var st = inEvent.currentTarget._scrollType;\n if (st === 0) {\n\n // this element is a `touch-action: none`, should never scroll\n ret = false;\n } else if (st === AUTO) {\n\n // this element is a `touch-action: auto`, should always scroll\n ret = true;\n } else {\n var t = inEvent.changedTouches[0];\n\n var dy = t.clientY - this.firstXY.Y;\n var dya = Math.abs(dy);\n var dx = t.clientX - this.firstXY.X;\n var dxa = Math.abs(dx);\n\n var up = st & UP;\n var down = st & DOWN;\n var left = st & LEFT;\n var right = st & RIGHT;\n\n if (left && right) {\n\n // should scroll on the x axis\n ret = dxa > dya;\n } else if (left) {\n\n // should scroll left\n ret = dxa > dya && dx > 0;\n } else if (right) {\n\n // should scroll right\n ret = dxa > dya && dx < 0;\n }\n\n if (!ret) {\n if (up && down) {\n\n // should scroll on the y axis\n ret = dxa < dya;\n } else if (up) {\n\n // should scroll up\n ret = dxa < dya && dy > 0;\n } else if (down) {\n\n // should scroll down\n ret = dxa < dya && dy < 0;\n }\n }\n\n }\n this.firstXY = null;\n return ret;\n }\n },\n findTouch: function(inTL, inId) {\n for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) {\n if (t.identifier === inId) {\n return true;\n }\n }\n },\n\n // In some instances, a touchstart can happen without a touchend. This\n // leaves the pointermap in a broken state.\n // Therefore, on every touchstart, we remove the touches that did not fire a\n // touchend event.\n // To keep state globally consistent, we fire a\n // pointercancel for this \"abandoned\" touch\n vacuumTouches: function(inEvent) {\n var tl = inEvent.touches;\n\n // pointermap.size should be < tl.length here, as the touchstart has not\n // been processed yet.\n if (pointermap$1.size >= tl.length) {\n var d = [];\n pointermap$1.forEach(function(value, key) {\n\n // Never remove pointerId == 1, which is mouse.\n // Touch identifiers are 2 smaller than their pointerId, which is the\n // index in pointermap.\n if (key !== 1 && !this.findTouch(tl, key - 2)) {\n var p = value.out;\n d.push(p);\n }\n }, this);\n d.forEach(this.cancelOut, this);\n }\n },\n touchstart: function(inEvent) {\n this.vacuumTouches(inEvent);\n this.setPrimaryTouch(inEvent.changedTouches[0]);\n this.dedupSynthMouse(inEvent);\n if (!this.scrolling) {\n this.processTouches(inEvent, this.overDown);\n }\n },\n overDown: function(inPointer) {\n pointermap$1.set(inPointer.pointerId, {\n target: inPointer.target,\n out: inPointer,\n outTarget: inPointer.target\n });\n dispatcher.enterOver(inPointer);\n dispatcher.down(inPointer);\n },\n\n // Called when pressure or tilt changes without the x/y changing\n touchforcechange: function(inEvent) {\n this.touchmove(inEvent);\n },\n touchmove: function(inEvent) {\n if (!this.scrolling) {\n if (this.shouldScroll(inEvent)) {\n this.scrolling = true;\n this.touchcancel(inEvent);\n } else {\n if (inEvent.type !== 'touchforcechange') {\n inEvent.preventDefault();\n }\n this.processTouches(inEvent, this.moveOverOut);\n }\n }\n },\n moveOverOut: function(inPointer) {\n var event = inPointer;\n var pointer = pointermap$1.get(event.pointerId);\n\n // a finger drifted off the screen, ignore it\n if (!pointer) {\n return;\n }\n var outEvent = pointer.out;\n var outTarget = pointer.outTarget;\n dispatcher.move(event);\n if (outEvent && outTarget !== event.target) {\n outEvent.relatedTarget = event.target;\n event.relatedTarget = outTarget;\n\n // recover from retargeting by shadow\n outEvent.target = outTarget;\n if (event.target) {\n dispatcher.leaveOut(outEvent);\n dispatcher.enterOver(event);\n } else {\n\n // clean up case when finger leaves the screen\n event.target = outTarget;\n event.relatedTarget = null;\n this.cancelOut(event);\n }\n }\n pointer.out = event;\n pointer.outTarget = event.target;\n },\n touchend: function(inEvent) {\n this.dedupSynthMouse(inEvent);\n this.processTouches(inEvent, this.upOut);\n },\n upOut: function(inPointer) {\n if (!this.scrolling) {\n dispatcher.up(inPointer);\n dispatcher.leaveOut(inPointer);\n }\n this.cleanUpPointer(inPointer);\n },\n touchcancel: function(inEvent) {\n this.processTouches(inEvent, this.cancelOut);\n },\n cancelOut: function(inPointer) {\n dispatcher.cancel(inPointer);\n dispatcher.leaveOut(inPointer);\n this.cleanUpPointer(inPointer);\n },\n cleanUpPointer: function(inPointer) {\n pointermap$1.delete(inPointer.pointerId);\n this.removePrimaryPointer(inPointer);\n },\n\n // prevent synth mouse events from creating pointer events\n dedupSynthMouse: function(inEvent) {\n var lts = mouseEvents.lastTouches;\n var t = inEvent.changedTouches[0];\n\n // only the primary finger will synth mouse events\n if (this.isPrimaryTouch(t)) {\n\n // remember x/y of last touch\n var lt = { x: t.clientX, y: t.clientY };\n lts.push(lt);\n var fn = (function(lts, lt) {\n var i = lts.indexOf(lt);\n if (i > -1) {\n lts.splice(i, 1);\n }\n }).bind(null, lts, lt);\n setTimeout(fn, DEDUP_TIMEOUT);\n }\n }\n };\n\n INSTALLER = new Installer(touchEvents.elementAdded, touchEvents.elementRemoved,\n touchEvents.elementChanged, touchEvents);\n\n var pointermap$2 = dispatcher.pointermap;\n var HAS_BITMAP_TYPE = window.MSPointerEvent &&\n typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE === 'number';\n var msEvents = {\n events: [\n 'MSPointerDown',\n 'MSPointerMove',\n 'MSPointerUp',\n 'MSPointerOut',\n 'MSPointerOver',\n 'MSPointerCancel',\n 'MSGotPointerCapture',\n 'MSLostPointerCapture'\n ],\n register: function(target) {\n dispatcher.listen(target, this.events);\n },\n unregister: function(target) {\n dispatcher.unlisten(target, this.events);\n },\n POINTER_TYPES: [\n '',\n 'unavailable',\n 'touch',\n 'pen',\n 'mouse'\n ],\n prepareEvent: function(inEvent) {\n var e = inEvent;\n if (HAS_BITMAP_TYPE) {\n e = dispatcher.cloneEvent(inEvent);\n e.pointerType = this.POINTER_TYPES[inEvent.pointerType];\n }\n return e;\n },\n cleanup: function(id) {\n pointermap$2.delete(id);\n },\n MSPointerDown: function(inEvent) {\n pointermap$2.set(inEvent.pointerId, inEvent);\n var e = this.prepareEvent(inEvent);\n dispatcher.down(e);\n },\n MSPointerMove: function(inEvent) {\n var e = this.prepareEvent(inEvent);\n dispatcher.move(e);\n },\n MSPointerUp: function(inEvent) {\n var e = this.prepareEvent(inEvent);\n dispatcher.up(e);\n this.cleanup(inEvent.pointerId);\n },\n MSPointerOut: function(inEvent) {\n var e = this.prepareEvent(inEvent);\n dispatcher.leaveOut(e);\n },\n MSPointerOver: function(inEvent) {\n var e = this.prepareEvent(inEvent);\n dispatcher.enterOver(e);\n },\n MSPointerCancel: function(inEvent) {\n var e = this.prepareEvent(inEvent);\n dispatcher.cancel(e);\n this.cleanup(inEvent.pointerId);\n },\n MSLostPointerCapture: function(inEvent) {\n var e = dispatcher.makeEvent('lostpointercapture', inEvent);\n dispatcher.dispatchEvent(e);\n },\n MSGotPointerCapture: function(inEvent) {\n var e = dispatcher.makeEvent('gotpointercapture', inEvent);\n dispatcher.dispatchEvent(e);\n }\n };\n\n function applyPolyfill() {\n\n // only activate if this platform does not have pointer events\n if (!window.PointerEvent) {\n window.PointerEvent = PointerEvent;\n\n if (window.navigator.msPointerEnabled) {\n var tp = window.navigator.msMaxTouchPoints;\n Object.defineProperty(window.navigator, 'maxTouchPoints', {\n value: tp,\n enumerable: true\n });\n dispatcher.registerSource('ms', msEvents);\n } else {\n Object.defineProperty(window.navigator, 'maxTouchPoints', {\n value: 0,\n enumerable: true\n });\n dispatcher.registerSource('mouse', mouseEvents);\n if (window.ontouchstart !== undefined) {\n dispatcher.registerSource('touch', touchEvents);\n }\n }\n\n dispatcher.register(document);\n }\n }\n\n var n = window.navigator;\n var s;\n var r;\n var h;\n function assertActive(id) {\n if (!dispatcher.pointermap.has(id)) {\n var error = new Error('NotFoundError');\n error.name = 'NotFoundError';\n throw error;\n }\n }\n function assertConnected(elem) {\n var parent = elem.parentNode;\n while (parent && parent !== elem.ownerDocument) {\n parent = parent.parentNode;\n }\n if (!parent) {\n var error = new Error('InvalidStateError');\n error.name = 'InvalidStateError';\n throw error;\n }\n }\n function inActiveButtonState(id) {\n var p = dispatcher.pointermap.get(id);\n return p.buttons !== 0;\n }\n if (n.msPointerEnabled) {\n s = function(pointerId) {\n assertActive(pointerId);\n assertConnected(this);\n if (inActiveButtonState(pointerId)) {\n dispatcher.setCapture(pointerId, this, true);\n this.msSetPointerCapture(pointerId);\n }\n };\n r = function(pointerId) {\n assertActive(pointerId);\n dispatcher.releaseCapture(pointerId, true);\n this.msReleasePointerCapture(pointerId);\n };\n } else {\n s = function setPointerCapture(pointerId) {\n assertActive(pointerId);\n assertConnected(this);\n if (inActiveButtonState(pointerId)) {\n dispatcher.setCapture(pointerId, this);\n }\n };\n r = function releasePointerCapture(pointerId) {\n assertActive(pointerId);\n dispatcher.releaseCapture(pointerId);\n };\n }\n h = function hasPointerCapture(pointerId) {\n return !!dispatcher.captureInfo[pointerId];\n };\n\n function applyPolyfill$1() {\n if (window.Element && !Element.prototype.setPointerCapture) {\n Object.defineProperties(Element.prototype, {\n 'setPointerCapture': {\n value: s\n },\n 'releasePointerCapture': {\n value: r\n },\n 'hasPointerCapture': {\n value: h\n }\n });\n }\n }\n\n applyAttributeStyles();\n applyPolyfill();\n applyPolyfill$1();\n\n var pointerevents = {\n dispatcher: dispatcher,\n Installer: Installer,\n PointerEvent: PointerEvent,\n PointerMap: PointerMap,\n targetFinding: targeting\n };\n\n return pointerevents;\n\n}));","'use strict';\n\nmodule.exports = Pbf;\n\nvar ieee754 = require('ieee754');\n\nfunction Pbf(buf) {\n this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);\n this.pos = 0;\n this.type = 0;\n this.length = this.buf.length;\n}\n\nPbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum\nPbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64\nPbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields\nPbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32\n\nvar SHIFT_LEFT_32 = (1 << 16) * (1 << 16),\n SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;\n\n// Threshold chosen based on both benchmarking and knowledge about browser string\n// data structures (which currently switch structure types at 12 bytes or more)\nvar TEXT_DECODER_MIN_LENGTH = 12;\nvar utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');\n\nPbf.prototype = {\n\n destroy: function() {\n this.buf = null;\n },\n\n // === READING =================================================================\n\n readFields: function(readField, result, end) {\n end = end || this.length;\n\n while (this.pos < end) {\n var val = this.readVarint(),\n tag = val >> 3,\n startPos = this.pos;\n\n this.type = val & 0x7;\n readField(tag, result, this);\n\n if (this.pos === startPos) this.skip(val);\n }\n return result;\n },\n\n readMessage: function(readField, result) {\n return this.readFields(readField, result, this.readVarint() + this.pos);\n },\n\n readFixed32: function() {\n var val = readUInt32(this.buf, this.pos);\n this.pos += 4;\n return val;\n },\n\n readSFixed32: function() {\n var val = readInt32(this.buf, this.pos);\n this.pos += 4;\n return val;\n },\n\n // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)\n\n readFixed64: function() {\n var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;\n this.pos += 8;\n return val;\n },\n\n readSFixed64: function() {\n var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;\n this.pos += 8;\n return val;\n },\n\n readFloat: function() {\n var val = ieee754.read(this.buf, this.pos, true, 23, 4);\n this.pos += 4;\n return val;\n },\n\n readDouble: function() {\n var val = ieee754.read(this.buf, this.pos, true, 52, 8);\n this.pos += 8;\n return val;\n },\n\n readVarint: function(isSigned) {\n var buf = this.buf,\n val, b;\n\n b = buf[this.pos++]; val = b & 0x7f; if (b < 0x80) return val;\n b = buf[this.pos++]; val |= (b & 0x7f) << 7; if (b < 0x80) return val;\n b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) return val;\n b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) return val;\n b = buf[this.pos]; val |= (b & 0x0f) << 28;\n\n return readVarintRemainder(val, isSigned, this);\n },\n\n readVarint64: function() { // for compatibility with v2.0.1\n return this.readVarint(true);\n },\n\n readSVarint: function() {\n var num = this.readVarint();\n return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding\n },\n\n readBoolean: function() {\n return Boolean(this.readVarint());\n },\n\n readString: function() {\n var end = this.readVarint() + this.pos;\n var pos = this.pos;\n this.pos = end;\n\n if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {\n // longer strings are fast with the built-in browser TextDecoder API\n return readUtf8TextDecoder(this.buf, pos, end);\n }\n // short strings are fast with our custom implementation\n return readUtf8(this.buf, pos, end);\n },\n\n readBytes: function() {\n var end = this.readVarint() + this.pos,\n buffer = this.buf.subarray(this.pos, end);\n this.pos = end;\n return buffer;\n },\n\n // verbose for performance reasons; doesn't affect gzipped size\n\n readPackedVarint: function(arr, isSigned) {\n if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned));\n var end = readPackedEnd(this);\n arr = arr || [];\n while (this.pos < end) arr.push(this.readVarint(isSigned));\n return arr;\n },\n readPackedSVarint: function(arr) {\n if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint());\n var end = readPackedEnd(this);\n arr = arr || [];\n while (this.pos < end) arr.push(this.readSVarint());\n return arr;\n },\n readPackedBoolean: function(arr) {\n if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean());\n var end = readPackedEnd(this);\n arr = arr || [];\n while (this.pos < end) arr.push(this.readBoolean());\n return arr;\n },\n readPackedFloat: function(arr) {\n if (this.type !== Pbf.Bytes) return arr.push(this.readFloat());\n var end = readPackedEnd(this);\n arr = arr || [];\n while (this.pos < end) arr.push(this.readFloat());\n return arr;\n },\n readPackedDouble: function(arr) {\n if (this.type !== Pbf.Bytes) return arr.push(this.readDouble());\n var end = readPackedEnd(this);\n arr = arr || [];\n while (this.pos < end) arr.push(this.readDouble());\n return arr;\n },\n readPackedFixed32: function(arr) {\n if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32());\n var end = readPackedEnd(this);\n arr = arr || [];\n while (this.pos < end) arr.push(this.readFixed32());\n return arr;\n },\n readPackedSFixed32: function(arr) {\n if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32());\n var end = readPackedEnd(this);\n arr = arr || [];\n while (this.pos < end) arr.push(this.readSFixed32());\n return arr;\n },\n readPackedFixed64: function(arr) {\n if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64());\n var end = readPackedEnd(this);\n arr = arr || [];\n while (this.pos < end) arr.push(this.readFixed64());\n return arr;\n },\n readPackedSFixed64: function(arr) {\n if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64());\n var end = readPackedEnd(this);\n arr = arr || [];\n while (this.pos < end) arr.push(this.readSFixed64());\n return arr;\n },\n\n skip: function(val) {\n var type = val & 0x7;\n if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {}\n else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos;\n else if (type === Pbf.Fixed32) this.pos += 4;\n else if (type === Pbf.Fixed64) this.pos += 8;\n else throw new Error('Unimplemented type: ' + type);\n },\n\n // === WRITING =================================================================\n\n writeTag: function(tag, type) {\n this.writeVarint((tag << 3) | type);\n },\n\n realloc: function(min) {\n var length = this.length || 16;\n\n while (length < this.pos + min) length *= 2;\n\n if (length !== this.length) {\n var buf = new Uint8Array(length);\n buf.set(this.buf);\n this.buf = buf;\n this.length = length;\n }\n },\n\n finish: function() {\n this.length = this.pos;\n this.pos = 0;\n return this.buf.subarray(0, this.length);\n },\n\n writeFixed32: function(val) {\n this.realloc(4);\n writeInt32(this.buf, val, this.pos);\n this.pos += 4;\n },\n\n writeSFixed32: function(val) {\n this.realloc(4);\n writeInt32(this.buf, val, this.pos);\n this.pos += 4;\n },\n\n writeFixed64: function(val) {\n this.realloc(8);\n writeInt32(this.buf, val & -1, this.pos);\n writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);\n this.pos += 8;\n },\n\n writeSFixed64: function(val) {\n this.realloc(8);\n writeInt32(this.buf, val & -1, this.pos);\n writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);\n this.pos += 8;\n },\n\n writeVarint: function(val) {\n val = +val || 0;\n\n if (val > 0xfffffff || val < 0) {\n writeBigVarint(val, this);\n return;\n }\n\n this.realloc(4);\n\n this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;\n this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;\n this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;\n this.buf[this.pos++] = (val >>> 7) & 0x7f;\n },\n\n writeSVarint: function(val) {\n this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);\n },\n\n writeBoolean: function(val) {\n this.writeVarint(Boolean(val));\n },\n\n writeString: function(str) {\n str = String(str);\n this.realloc(str.length * 4);\n\n this.pos++; // reserve 1 byte for short string length\n\n var startPos = this.pos;\n // write the string directly to the buffer and see how much was written\n this.pos = writeUtf8(this.buf, str, this.pos);\n var len = this.pos - startPos;\n\n if (len >= 0x80) makeRoomForExtraLength(startPos, len, this);\n\n // finally, write the message length in the reserved place and restore the position\n this.pos = startPos - 1;\n this.writeVarint(len);\n this.pos += len;\n },\n\n writeFloat: function(val) {\n this.realloc(4);\n ieee754.write(this.buf, val, this.pos, true, 23, 4);\n this.pos += 4;\n },\n\n writeDouble: function(val) {\n this.realloc(8);\n ieee754.write(this.buf, val, this.pos, true, 52, 8);\n this.pos += 8;\n },\n\n writeBytes: function(buffer) {\n var len = buffer.length;\n this.writeVarint(len);\n this.realloc(len);\n for (var i = 0; i < len; i++) this.buf[this.pos++] = buffer[i];\n },\n\n writeRawMessage: function(fn, obj) {\n this.pos++; // reserve 1 byte for short message length\n\n // write the message directly to the buffer and see how much was written\n var startPos = this.pos;\n fn(obj, this);\n var len = this.pos - startPos;\n\n if (len >= 0x80) makeRoomForExtraLength(startPos, len, this);\n\n // finally, write the message length in the reserved place and restore the position\n this.pos = startPos - 1;\n this.writeVarint(len);\n this.pos += len;\n },\n\n writeMessage: function(tag, fn, obj) {\n this.writeTag(tag, Pbf.Bytes);\n this.writeRawMessage(fn, obj);\n },\n\n writePackedVarint: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedVarint, arr); },\n writePackedSVarint: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSVarint, arr); },\n writePackedBoolean: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedBoolean, arr); },\n writePackedFloat: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFloat, arr); },\n writePackedDouble: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedDouble, arr); },\n writePackedFixed32: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed32, arr); },\n writePackedSFixed32: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed32, arr); },\n writePackedFixed64: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed64, arr); },\n writePackedSFixed64: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed64, arr); },\n\n writeBytesField: function(tag, buffer) {\n this.writeTag(tag, Pbf.Bytes);\n this.writeBytes(buffer);\n },\n writeFixed32Field: function(tag, val) {\n this.writeTag(tag, Pbf.Fixed32);\n this.writeFixed32(val);\n },\n writeSFixed32Field: function(tag, val) {\n this.writeTag(tag, Pbf.Fixed32);\n this.writeSFixed32(val);\n },\n writeFixed64Field: function(tag, val) {\n this.writeTag(tag, Pbf.Fixed64);\n this.writeFixed64(val);\n },\n writeSFixed64Field: function(tag, val) {\n this.writeTag(tag, Pbf.Fixed64);\n this.writeSFixed64(val);\n },\n writeVarintField: function(tag, val) {\n this.writeTag(tag, Pbf.Varint);\n this.writeVarint(val);\n },\n writeSVarintField: function(tag, val) {\n this.writeTag(tag, Pbf.Varint);\n this.writeSVarint(val);\n },\n writeStringField: function(tag, str) {\n this.writeTag(tag, Pbf.Bytes);\n this.writeString(str);\n },\n writeFloatField: function(tag, val) {\n this.writeTag(tag, Pbf.Fixed32);\n this.writeFloat(val);\n },\n writeDoubleField: function(tag, val) {\n this.writeTag(tag, Pbf.Fixed64);\n this.writeDouble(val);\n },\n writeBooleanField: function(tag, val) {\n this.writeVarintField(tag, Boolean(val));\n }\n};\n\nfunction readVarintRemainder(l, s, p) {\n var buf = p.buf,\n h, b;\n\n b = buf[p.pos++]; h = (b & 0x70) >> 4; if (b < 0x80) return toNum(l, h, s);\n b = buf[p.pos++]; h |= (b & 0x7f) << 3; if (b < 0x80) return toNum(l, h, s);\n b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) return toNum(l, h, s);\n b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) return toNum(l, h, s);\n b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) return toNum(l, h, s);\n b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) return toNum(l, h, s);\n\n throw new Error('Expected varint not more than 10 bytes');\n}\n\nfunction readPackedEnd(pbf) {\n return pbf.type === Pbf.Bytes ?\n pbf.readVarint() + pbf.pos : pbf.pos + 1;\n}\n\nfunction toNum(low, high, isSigned) {\n if (isSigned) {\n return high * 0x100000000 + (low >>> 0);\n }\n\n return ((high >>> 0) * 0x100000000) + (low >>> 0);\n}\n\nfunction writeBigVarint(val, pbf) {\n var low, high;\n\n if (val >= 0) {\n low = (val % 0x100000000) | 0;\n high = (val / 0x100000000) | 0;\n } else {\n low = ~(-val % 0x100000000);\n high = ~(-val / 0x100000000);\n\n if (low ^ 0xffffffff) {\n low = (low + 1) | 0;\n } else {\n low = 0;\n high = (high + 1) | 0;\n }\n }\n\n if (val >= 0x10000000000000000 || val < -0x10000000000000000) {\n throw new Error('Given varint doesn\\'t fit into 10 bytes');\n }\n\n pbf.realloc(10);\n\n writeBigVarintLow(low, high, pbf);\n writeBigVarintHigh(high, pbf);\n}\n\nfunction writeBigVarintLow(low, high, pbf) {\n pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;\n pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;\n pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;\n pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;\n pbf.buf[pbf.pos] = low & 0x7f;\n}\n\nfunction writeBigVarintHigh(high, pbf) {\n var lsb = (high & 0x07) << 4;\n\n pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0); if (!high) return;\n pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;\n pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;\n pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;\n pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;\n pbf.buf[pbf.pos++] = high & 0x7f;\n}\n\nfunction makeRoomForExtraLength(startPos, len, pbf) {\n var extraLen =\n len <= 0x3fff ? 1 :\n len <= 0x1fffff ? 2 :\n len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7));\n\n // if 1 byte isn't enough for encoding message length, shift the data to the right\n pbf.realloc(extraLen);\n for (var i = pbf.pos - 1; i >= startPos; i--) pbf.buf[i + extraLen] = pbf.buf[i];\n}\n\nfunction writePackedVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeVarint(arr[i]); }\nfunction writePackedSVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSVarint(arr[i]); }\nfunction writePackedFloat(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFloat(arr[i]); }\nfunction writePackedDouble(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeDouble(arr[i]); }\nfunction writePackedBoolean(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeBoolean(arr[i]); }\nfunction writePackedFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed32(arr[i]); }\nfunction writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed32(arr[i]); }\nfunction writePackedFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed64(arr[i]); }\nfunction writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed64(arr[i]); }\n\n// Buffer code below from https://github.com/feross/buffer, MIT-licensed\n\nfunction readUInt32(buf, pos) {\n return ((buf[pos]) |\n (buf[pos + 1] << 8) |\n (buf[pos + 2] << 16)) +\n (buf[pos + 3] * 0x1000000);\n}\n\nfunction writeInt32(buf, val, pos) {\n buf[pos] = val;\n buf[pos + 1] = (val >>> 8);\n buf[pos + 2] = (val >>> 16);\n buf[pos + 3] = (val >>> 24);\n}\n\nfunction readInt32(buf, pos) {\n return ((buf[pos]) |\n (buf[pos + 1] << 8) |\n (buf[pos + 2] << 16)) +\n (buf[pos + 3] << 24);\n}\n\nfunction readUtf8(buf, pos, end) {\n var str = '';\n var i = pos;\n\n while (i < end) {\n var b0 = buf[i];\n var c = null; // codepoint\n var bytesPerSequence =\n b0 > 0xEF ? 4 :\n b0 > 0xDF ? 3 :\n b0 > 0xBF ? 2 : 1;\n\n if (i + bytesPerSequence > end) break;\n\n var b1, b2, b3;\n\n if (bytesPerSequence === 1) {\n if (b0 < 0x80) {\n c = b0;\n }\n } else if (bytesPerSequence === 2) {\n b1 = buf[i + 1];\n if ((b1 & 0xC0) === 0x80) {\n c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F);\n if (c <= 0x7F) {\n c = null;\n }\n }\n } else if (bytesPerSequence === 3) {\n b1 = buf[i + 1];\n b2 = buf[i + 2];\n if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {\n c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F);\n if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) {\n c = null;\n }\n }\n } else if (bytesPerSequence === 4) {\n b1 = buf[i + 1];\n b2 = buf[i + 2];\n b3 = buf[i + 3];\n if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {\n c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F);\n if (c <= 0xFFFF || c >= 0x110000) {\n c = null;\n }\n }\n }\n\n if (c === null) {\n c = 0xFFFD;\n bytesPerSequence = 1;\n\n } else if (c > 0xFFFF) {\n c -= 0x10000;\n str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);\n c = 0xDC00 | c & 0x3FF;\n }\n\n str += String.fromCharCode(c);\n i += bytesPerSequence;\n }\n\n return str;\n}\n\nfunction readUtf8TextDecoder(buf, pos, end) {\n return utf8TextDecoder.decode(buf.subarray(pos, end));\n}\n\nfunction writeUtf8(buf, str, pos) {\n for (var i = 0, c, lead; i < str.length; i++) {\n c = str.charCodeAt(i); // code point\n\n if (c > 0xD7FF && c < 0xE000) {\n if (lead) {\n if (c < 0xDC00) {\n buf[pos++] = 0xEF;\n buf[pos++] = 0xBF;\n buf[pos++] = 0xBD;\n lead = c;\n continue;\n } else {\n c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;\n lead = null;\n }\n } else {\n if (c > 0xDBFF || (i + 1 === str.length)) {\n buf[pos++] = 0xEF;\n buf[pos++] = 0xBF;\n buf[pos++] = 0xBD;\n } else {\n lead = c;\n }\n continue;\n }\n } else if (lead) {\n buf[pos++] = 0xEF;\n buf[pos++] = 0xBF;\n buf[pos++] = 0xBD;\n lead = null;\n }\n\n if (c < 0x80) {\n buf[pos++] = c;\n } else {\n if (c < 0x800) {\n buf[pos++] = c >> 0x6 | 0xC0;\n } else {\n if (c < 0x10000) {\n buf[pos++] = c >> 0xC | 0xE0;\n } else {\n buf[pos++] = c >> 0x12 | 0xF0;\n buf[pos++] = c >> 0xC & 0x3F | 0x80;\n }\n buf[pos++] = c >> 0x6 & 0x3F | 0x80;\n }\n buf[pos++] = c & 0x3F | 0x80;\n }\n }\n return pos;\n}\n","var Processor = require('./processor');\n\nexports.Processor = Processor;\n","exports.read = function (buffer, offset, isLE, mLen, nBytes) {\n var e, m\n var eLen = (nBytes * 8) - mLen - 1\n var eMax = (1 << eLen) - 1\n var eBias = eMax >> 1\n var nBits = -7\n var i = isLE ? (nBytes - 1) : 0\n var d = isLE ? -1 : 1\n var s = buffer[offset + i]\n\n i += d\n\n e = s & ((1 << (-nBits)) - 1)\n s >>= (-nBits)\n nBits += eLen\n for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n m = e & ((1 << (-nBits)) - 1)\n e >>= (-nBits)\n nBits += mLen\n for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n if (e === 0) {\n e = 1 - eBias\n } else if (e === eMax) {\n return m ? NaN : ((s ? -1 : 1) * Infinity)\n } else {\n m = m + Math.pow(2, mLen)\n e = e - eBias\n }\n return (s ? -1 : 1) * m * Math.pow(2, e - mLen)\n}\n\nexports.write = function (buffer, value, offset, isLE, mLen, nBytes) {\n var e, m, c\n var eLen = (nBytes * 8) - mLen - 1\n var eMax = (1 << eLen) - 1\n var eBias = eMax >> 1\n var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)\n var i = isLE ? 0 : (nBytes - 1)\n var d = isLE ? 1 : -1\n var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0\n\n value = Math.abs(value)\n\n if (isNaN(value) || value === Infinity) {\n m = isNaN(value) ? 1 : 0\n e = eMax\n } else {\n e = Math.floor(Math.log(value) / Math.LN2)\n if (value * (c = Math.pow(2, -e)) < 1) {\n e--\n c *= 2\n }\n if (e + eBias >= 1) {\n value += rt / c\n } else {\n value += rt * Math.pow(2, 1 - eBias)\n }\n if (value * c >= 2) {\n e++\n c /= 2\n }\n\n if (e + eBias >= eMax) {\n m = 0\n e = eMax\n } else if (e + eBias >= 1) {\n m = ((value * c) - 1) * Math.pow(2, mLen)\n e = e + eBias\n } else {\n m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)\n e = 0\n }\n }\n\n for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}\n\n e = (e << mLen) | m\n eLen += mLen\n for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}\n\n buffer[offset + i - d] |= s * 128\n}\n","var newImageData = require('./util').newImageData;\n\n/**\n * Create a function for running operations. This function is serialized for\n * use in a worker.\n * @param {function(Array, Object):*} operation The operation.\n * @return {function(Object):ArrayBuffer} A function that takes an object with\n * buffers, meta, imageOps, width, and height properties and returns an array\n * buffer.\n */\nfunction createMinion(operation) {\n var workerHasImageData = true;\n try {\n new ImageData(10, 10);\n } catch (_) {\n workerHasImageData = false;\n }\n\n function newWorkerImageData(data, width, height) {\n if (workerHasImageData) {\n return new ImageData(data, width, height);\n } else {\n return {data: data, width: width, height: height};\n }\n }\n\n return function(data) {\n // bracket notation for minification support\n var buffers = data['buffers'];\n var meta = data['meta'];\n var imageOps = data['imageOps'];\n var width = data['width'];\n var height = data['height'];\n\n var numBuffers = buffers.length;\n var numBytes = buffers[0].byteLength;\n var output, b;\n\n if (imageOps) {\n var images = new Array(numBuffers);\n for (b = 0; b < numBuffers; ++b) {\n images[b] = newWorkerImageData(\n new Uint8ClampedArray(buffers[b]), width, height);\n }\n output = operation(images, meta).data;\n } else {\n output = new Uint8ClampedArray(numBytes);\n var arrays = new Array(numBuffers);\n var pixels = new Array(numBuffers);\n for (b = 0; b < numBuffers; ++b) {\n arrays[b] = new Uint8ClampedArray(buffers[b]);\n pixels[b] = [0, 0, 0, 0];\n }\n for (var i = 0; i < numBytes; i += 4) {\n for (var j = 0; j < numBuffers; ++j) {\n var array = arrays[j];\n pixels[j][0] = array[i];\n pixels[j][1] = array[i + 1];\n pixels[j][2] = array[i + 2];\n pixels[j][3] = array[i + 3];\n }\n var pixel = operation(pixels, meta);\n output[i] = pixel[0];\n output[i + 1] = pixel[1];\n output[i + 2] = pixel[2];\n output[i + 3] = pixel[3];\n }\n }\n return output.buffer;\n };\n}\n\n/**\n * Create a worker for running operations.\n * @param {Object} config Configuration.\n * @param {function(MessageEvent)} onMessage Called with a message event.\n * @return {Worker} The worker.\n */\nfunction createWorker(config, onMessage) {\n var lib = Object.keys(config.lib || {}).map(function(name) {\n return 'var ' + name + ' = ' + config.lib[name].toString() + ';';\n });\n\n var lines = lib.concat([\n 'var __minion__ = (' + createMinion.toString() + ')(', config.operation.toString(), ');',\n 'self.addEventListener(\"message\", function(event) {',\n ' var buffer = __minion__(event.data);',\n ' self.postMessage({buffer: buffer, meta: event.data.meta}, [buffer]);',\n '});'\n ]);\n\n var blob = new Blob(lines, {type: 'text/javascript'});\n var source = URL.createObjectURL(blob);\n var worker = new Worker(source);\n worker.addEventListener('message', onMessage);\n return worker;\n}\n\n/**\n * Create a faux worker for running operations.\n * @param {Object} config Configuration.\n * @param {function(MessageEvent)} onMessage Called with a message event.\n * @return {Object} The faux worker.\n */\nfunction createFauxWorker(config, onMessage) {\n var minion = createMinion(config.operation);\n return {\n postMessage: function(data) {\n setTimeout(function() {\n onMessage({'data': {'buffer': minion(data), 'meta': data['meta']}});\n }, 0);\n }\n };\n}\n\n/**\n * A processor runs pixel or image operations in workers.\n * @param {Object} config Configuration.\n */\nfunction Processor(config) {\n this._imageOps = !!config.imageOps;\n var threads;\n if (config.threads === 0) {\n threads = 0;\n } else if (this._imageOps) {\n threads = 1;\n } else {\n threads = config.threads || 1;\n }\n var workers = [];\n if (threads) {\n for (var i = 0; i < threads; ++i) {\n workers[i] = createWorker(config, this._onWorkerMessage.bind(this, i));\n }\n } else {\n workers[0] = createFauxWorker(config, this._onWorkerMessage.bind(this, 0));\n }\n this._workers = workers;\n this._queue = [];\n this._maxQueueLength = config.queue || Infinity;\n this._running = 0;\n this._dataLookup = {};\n this._job = null;\n}\n\n/**\n * Run operation on input data.\n * @param {Array.} inputs Array of pixels or image data\n * (depending on the operation type).\n * @param {Object} meta A user data object. This is passed to all operations\n * and must be serializable.\n * @param {function(Error, ImageData, Object)} callback Called when work\n * completes. The first argument is any error. The second is the ImageData\n * generated by operations. The third is the user data object.\n */\nProcessor.prototype.process = function(inputs, meta, callback) {\n this._enqueue({\n inputs: inputs,\n meta: meta,\n callback: callback\n });\n this._dispatch();\n};\n\n/**\n * Stop responding to any completed work and destroy the processor.\n */\nProcessor.prototype.destroy = function() {\n for (var key in this) {\n this[key] = null;\n }\n this._destroyed = true;\n};\n\n/**\n * Add a job to the queue.\n * @param {Object} job The job.\n */\nProcessor.prototype._enqueue = function(job) {\n this._queue.push(job);\n while (this._queue.length > this._maxQueueLength) {\n this._queue.shift().callback(null, null);\n }\n};\n\n/**\n * Dispatch a job.\n */\nProcessor.prototype._dispatch = function() {\n if (this._running === 0 && this._queue.length > 0) {\n var job = this._job = this._queue.shift();\n var width = job.inputs[0].width;\n var height = job.inputs[0].height;\n var buffers = job.inputs.map(function(input) {\n return input.data.buffer;\n });\n var threads = this._workers.length;\n this._running = threads;\n if (threads === 1) {\n this._workers[0].postMessage({\n 'buffers': buffers,\n 'meta': job.meta,\n 'imageOps': this._imageOps,\n 'width': width,\n 'height': height\n }, buffers);\n } else {\n var length = job.inputs[0].data.length;\n var segmentLength = 4 * Math.ceil(length / 4 / threads);\n for (var i = 0; i < threads; ++i) {\n var offset = i * segmentLength;\n var slices = [];\n for (var j = 0, jj = buffers.length; j < jj; ++j) {\n slices.push(buffers[i].slice(offset, offset + segmentLength));\n }\n this._workers[i].postMessage({\n 'buffers': slices,\n 'meta': job.meta,\n 'imageOps': this._imageOps,\n 'width': width,\n 'height': height\n }, slices);\n }\n }\n }\n};\n\n/**\n * Handle messages from the worker.\n * @param {number} index The worker index.\n * @param {MessageEvent} event The message event.\n */\nProcessor.prototype._onWorkerMessage = function(index, event) {\n if (this._destroyed) {\n return;\n }\n this._dataLookup[index] = event.data;\n --this._running;\n if (this._running === 0) {\n this._resolveJob();\n }\n};\n\n/**\n * Resolve a job. If there are no more worker threads, the processor callback\n * will be called.\n */\nProcessor.prototype._resolveJob = function() {\n var job = this._job;\n var threads = this._workers.length;\n var data, meta;\n if (threads === 1) {\n data = new Uint8ClampedArray(this._dataLookup[0]['buffer']);\n meta = this._dataLookup[0]['meta'];\n } else {\n var length = job.inputs[0].data.length;\n data = new Uint8ClampedArray(length);\n meta = new Array(length);\n var segmentLength = 4 * Math.ceil(length / 4 / threads);\n for (var i = 0; i < threads; ++i) {\n var buffer = this._dataLookup[i]['buffer'];\n var offset = i * segmentLength;\n data.set(new Uint8ClampedArray(buffer), offset);\n meta[i] = this._dataLookup[i]['meta'];\n }\n }\n this._job = null;\n this._dataLookup = {};\n job.callback(null,\n newImageData(data, job.inputs[0].width, job.inputs[0].height), meta);\n this._dispatch();\n};\n\nmodule.exports = Processor;\n","var hasImageData = true;\ntry {\n new ImageData(10, 10);\n} catch (_) {\n hasImageData = false;\n}\n\nvar context = document.createElement('canvas').getContext('2d');\n\nfunction newImageData(data, width, height) {\n if (hasImageData) {\n return new ImageData(data, width, height);\n } else {\n var imageData = context.createImageData(width, height);\n imageData.data.set(data);\n return imageData;\n }\n}\n\nexports.newImageData = newImageData;\n","/**\n * @module ol/util\n */\n/**\n * @return {?} Any return.\n */\nexport function abstract() {\n return /** @type {?} */ ((function () {\n throw new Error('Unimplemented abstract method.');\n })());\n}\n/**\n * Counter for getUid.\n * @type {number}\n * @private\n */\nvar uidCounter_ = 0;\n/**\n * Gets a unique ID for an object. This mutates the object so that further calls\n * with the same object as a parameter returns the same value. Unique IDs are generated\n * as a strictly increasing sequence. Adapted from goog.getUid.\n *\n * @param {Object} obj The object to get the unique ID for.\n * @return {string} The unique ID for the object.\n * @api\n */\nexport function getUid(obj) {\n return obj.ol_uid || (obj.ol_uid = String(++uidCounter_));\n}\n/**\n * OpenLayers version.\n * @type {string}\n */\nexport var VERSION = '6.1.1';\n//# sourceMappingURL=util.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/AssertionError\n */\nimport { VERSION } from './util.js';\n/**\n * Error object thrown when an assertion failed. This is an ECMA-262 Error,\n * extended with a `code` property.\n * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error.\n */\nvar AssertionError = /** @class */ (function (_super) {\n __extends(AssertionError, _super);\n /**\n * @param {number} code Error code.\n */\n function AssertionError(code) {\n var _this = this;\n var path = VERSION === 'latest' ? VERSION : 'v' + VERSION.split('-')[0];\n var message = 'Assertion failed. See https://openlayers.org/en/' + path +\n '/doc/errors/#' + code + ' for details.';\n _this = _super.call(this, message) || this;\n /**\n * Error code. The meaning of the code can be found on\n * https://openlayers.org/en/latest/doc/errors/ (replace `latest` with\n * the version found in the OpenLayers script's header comment if a version\n * other than the latest is used).\n * @type {number}\n * @api\n */\n _this.code = code;\n /**\n * @type {string}\n */\n _this.name = 'AssertionError';\n // Re-assign message, see https://github.com/Rich-Harris/buble/issues/40\n _this.message = message;\n return _this;\n }\n return AssertionError;\n}(Error));\nexport default AssertionError;\n//# sourceMappingURL=AssertionError.js.map","/**\n * @module ol/CollectionEventType\n */\n/**\n * @enum {string}\n */\nexport default {\n /**\n * Triggered when an item is added to the collection.\n * @event module:ol/Collection.CollectionEvent#add\n * @api\n */\n ADD: 'add',\n /**\n * Triggered when an item is removed from the collection.\n * @event module:ol/Collection.CollectionEvent#remove\n * @api\n */\n REMOVE: 'remove'\n};\n//# sourceMappingURL=CollectionEventType.js.map","/**\n * @module ol/ObjectEventType\n */\n/**\n * @enum {string}\n */\nexport default {\n /**\n * Triggered when a property is changed.\n * @event module:ol/Object.ObjectEvent#propertychange\n * @api\n */\n PROPERTYCHANGE: 'propertychange'\n};\n//# sourceMappingURL=ObjectEventType.js.map","/**\n * @module ol/obj\n */\n/**\n * Polyfill for Object.assign(). Assigns enumerable and own properties from\n * one or more source objects to a target object.\n * See https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign.\n *\n * @param {!Object} target The target object.\n * @param {...Object} var_sources The source object(s).\n * @return {!Object} The modified target object.\n */\nexport var assign = (typeof Object.assign === 'function') ? Object.assign : function (target, var_sources) {\n if (target === undefined || target === null) {\n throw new TypeError('Cannot convert undefined or null to object');\n }\n var output = Object(target);\n for (var i = 1, ii = arguments.length; i < ii; ++i) {\n var source = arguments[i];\n if (source !== undefined && source !== null) {\n for (var key in source) {\n if (source.hasOwnProperty(key)) {\n output[key] = source[key];\n }\n }\n }\n }\n return output;\n};\n/**\n * Removes all properties from an object.\n * @param {Object} object The object to clear.\n */\nexport function clear(object) {\n for (var property in object) {\n delete object[property];\n }\n}\n/**\n * Polyfill for Object.values(). Get an array of property values from an object.\n * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values\n *\n * @param {!Object} object The object from which to get the values.\n * @return {!Array} The property values.\n * @template K,V\n */\nexport var getValues = (typeof Object.values === 'function') ? Object.values : function (object) {\n var values = [];\n for (var property in object) {\n values.push(object[property]);\n }\n return values;\n};\n/**\n * Determine if an object has any properties.\n * @param {Object} object The object to check.\n * @return {boolean} The object is empty.\n */\nexport function isEmpty(object) {\n var property;\n for (property in object) {\n return false;\n }\n return !property;\n}\n//# sourceMappingURL=obj.js.map","/**\n * @module ol/events\n */\nimport { clear } from './obj.js';\n/**\n * Key to use with {@link module:ol/Observable~Observable#unByKey}.\n * @typedef {Object} EventsKey\n * @property {ListenerFunction} listener\n * @property {import(\"./events/Target.js\").EventTargetLike} target\n * @property {string} type\n * @api\n */\n/**\n * Listener function. This function is called with an event object as argument.\n * When the function returns `false`, event propagation will stop.\n *\n * @typedef {function((Event|import(\"./events/Event.js\").default)): (void|boolean)} ListenerFunction\n * @api\n */\n/**\n * Registers an event listener on an event target. Inspired by\n * https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html\n *\n * This function efficiently binds a `listener` to a `this` object, and returns\n * a key for use with {@link module:ol/events~unlistenByKey}.\n *\n * @param {import(\"./events/Target.js\").EventTargetLike} target Event target.\n * @param {string} type Event type.\n * @param {ListenerFunction} listener Listener.\n * @param {Object=} opt_this Object referenced by the `this` keyword in the\n * listener. Default is the `target`.\n * @param {boolean=} opt_once If true, add the listener as one-off listener.\n * @return {EventsKey} Unique key for the listener.\n */\nexport function listen(target, type, listener, opt_this, opt_once) {\n if (opt_this && opt_this !== target) {\n listener = listener.bind(opt_this);\n }\n if (opt_once) {\n var originalListener_1 = listener;\n listener = function () {\n target.removeEventListener(type, listener);\n originalListener_1.apply(this, arguments);\n };\n }\n var eventsKey = {\n target: target,\n type: type,\n listener: listener\n };\n target.addEventListener(type, listener);\n return eventsKey;\n}\n/**\n * Registers a one-off event listener on an event target. Inspired by\n * https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html\n *\n * This function efficiently binds a `listener` as self-unregistering listener\n * to a `this` object, and returns a key for use with\n * {@link module:ol/events~unlistenByKey} in case the listener needs to be\n * unregistered before it is called.\n *\n * When {@link module:ol/events~listen} is called with the same arguments after this\n * function, the self-unregistering listener will be turned into a permanent\n * listener.\n *\n * @param {import(\"./events/Target.js\").EventTargetLike} target Event target.\n * @param {string} type Event type.\n * @param {ListenerFunction} listener Listener.\n * @param {Object=} opt_this Object referenced by the `this` keyword in the\n * listener. Default is the `target`.\n * @return {EventsKey} Key for unlistenByKey.\n */\nexport function listenOnce(target, type, listener, opt_this) {\n return listen(target, type, listener, opt_this, true);\n}\n/**\n * Unregisters event listeners on an event target. Inspired by\n * https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html\n *\n * The argument passed to this function is the key returned from\n * {@link module:ol/events~listen} or {@link module:ol/events~listenOnce}.\n *\n * @param {EventsKey} key The key.\n */\nexport function unlistenByKey(key) {\n if (key && key.target) {\n key.target.removeEventListener(key.type, key.listener);\n clear(key);\n }\n}\n//# sourceMappingURL=events.js.map","/**\n * @module ol/Disposable\n */\n/**\n * @classdesc\n * Objects that need to clean up after themselves.\n */\nvar Disposable = /** @class */ (function () {\n function Disposable() {\n /**\n * The object has already been disposed.\n * @type {boolean}\n * @private\n */\n this.disposed_ = false;\n }\n /**\n * Clean up.\n */\n Disposable.prototype.dispose = function () {\n if (!this.disposed_) {\n this.disposed_ = true;\n this.disposeInternal();\n }\n };\n /**\n * Extension point for disposable objects.\n * @protected\n */\n Disposable.prototype.disposeInternal = function () { };\n return Disposable;\n}());\nexport default Disposable;\n//# sourceMappingURL=Disposable.js.map","/**\n * @module ol/array\n */\n/**\n * Performs a binary search on the provided sorted list and returns the index of the item if found. If it can't be found it'll return -1.\n * https://github.com/darkskyapp/binary-search\n *\n * @param {Array<*>} haystack Items to search through.\n * @param {*} needle The item to look for.\n * @param {Function=} opt_comparator Comparator function.\n * @return {number} The index of the item if found, -1 if not.\n */\nexport function binarySearch(haystack, needle, opt_comparator) {\n var mid, cmp;\n var comparator = opt_comparator || numberSafeCompareFunction;\n var low = 0;\n var high = haystack.length;\n var found = false;\n while (low < high) {\n /* Note that \"(low + high) >>> 1\" may overflow, and results in a typecast\n * to double (which gives the wrong results). */\n mid = low + (high - low >> 1);\n cmp = +comparator(haystack[mid], needle);\n if (cmp < 0.0) { /* Too low. */\n low = mid + 1;\n }\n else { /* Key found or too high */\n high = mid;\n found = !cmp;\n }\n }\n /* Key not found. */\n return found ? low : ~low;\n}\n/**\n * Compare function for array sort that is safe for numbers.\n * @param {*} a The first object to be compared.\n * @param {*} b The second object to be compared.\n * @return {number} A negative number, zero, or a positive number as the first\n * argument is less than, equal to, or greater than the second.\n */\nexport function numberSafeCompareFunction(a, b) {\n return a > b ? 1 : a < b ? -1 : 0;\n}\n/**\n * Whether the array contains the given object.\n * @param {Array<*>} arr The array to test for the presence of the element.\n * @param {*} obj The object for which to test.\n * @return {boolean} The object is in the array.\n */\nexport function includes(arr, obj) {\n return arr.indexOf(obj) >= 0;\n}\n/**\n * @param {Array} arr Array.\n * @param {number} target Target.\n * @param {number} direction 0 means return the nearest, > 0\n * means return the largest nearest, < 0 means return the\n * smallest nearest.\n * @return {number} Index.\n */\nexport function linearFindNearest(arr, target, direction) {\n var n = arr.length;\n if (arr[0] <= target) {\n return 0;\n }\n else if (target <= arr[n - 1]) {\n return n - 1;\n }\n else {\n var i = void 0;\n if (direction > 0) {\n for (i = 1; i < n; ++i) {\n if (arr[i] < target) {\n return i - 1;\n }\n }\n }\n else if (direction < 0) {\n for (i = 1; i < n; ++i) {\n if (arr[i] <= target) {\n return i;\n }\n }\n }\n else {\n for (i = 1; i < n; ++i) {\n if (arr[i] == target) {\n return i;\n }\n else if (arr[i] < target) {\n if (arr[i - 1] - target < target - arr[i]) {\n return i - 1;\n }\n else {\n return i;\n }\n }\n }\n }\n return n - 1;\n }\n}\n/**\n * @param {Array<*>} arr Array.\n * @param {number} begin Begin index.\n * @param {number} end End index.\n */\nexport function reverseSubArray(arr, begin, end) {\n while (begin < end) {\n var tmp = arr[begin];\n arr[begin] = arr[end];\n arr[end] = tmp;\n ++begin;\n --end;\n }\n}\n/**\n * @param {Array} arr The array to modify.\n * @param {!Array|VALUE} data The elements or arrays of elements to add to arr.\n * @template VALUE\n */\nexport function extend(arr, data) {\n var extension = Array.isArray(data) ? data : [data];\n var length = extension.length;\n for (var i = 0; i < length; i++) {\n arr[arr.length] = extension[i];\n }\n}\n/**\n * @param {Array} arr The array to modify.\n * @param {VALUE} obj The element to remove.\n * @template VALUE\n * @return {boolean} If the element was removed.\n */\nexport function remove(arr, obj) {\n var i = arr.indexOf(obj);\n var found = i > -1;\n if (found) {\n arr.splice(i, 1);\n }\n return found;\n}\n/**\n * @param {Array} arr The array to search in.\n * @param {function(VALUE, number, ?) : boolean} func The function to compare.\n * @template VALUE\n * @return {VALUE|null} The element found or null.\n */\nexport function find(arr, func) {\n var length = arr.length >>> 0;\n var value;\n for (var i = 0; i < length; i++) {\n value = arr[i];\n if (func(value, i, arr)) {\n return value;\n }\n }\n return null;\n}\n/**\n * @param {Array|Uint8ClampedArray} arr1 The first array to compare.\n * @param {Array|Uint8ClampedArray} arr2 The second array to compare.\n * @return {boolean} Whether the two arrays are equal.\n */\nexport function equals(arr1, arr2) {\n var len1 = arr1.length;\n if (len1 !== arr2.length) {\n return false;\n }\n for (var i = 0; i < len1; i++) {\n if (arr1[i] !== arr2[i]) {\n return false;\n }\n }\n return true;\n}\n/**\n * Sort the passed array such that the relative order of equal elements is preverved.\n * See https://en.wikipedia.org/wiki/Sorting_algorithm#Stability for details.\n * @param {Array<*>} arr The array to sort (modifies original).\n * @param {!function(*, *): number} compareFnc Comparison function.\n * @api\n */\nexport function stableSort(arr, compareFnc) {\n var length = arr.length;\n var tmp = Array(arr.length);\n var i;\n for (i = 0; i < length; i++) {\n tmp[i] = { index: i, value: arr[i] };\n }\n tmp.sort(function (a, b) {\n return compareFnc(a.value, b.value) || a.index - b.index;\n });\n for (i = 0; i < arr.length; i++) {\n arr[i] = tmp[i].value;\n }\n}\n/**\n * @param {Array<*>} arr The array to search in.\n * @param {Function} func Comparison function.\n * @return {number} Return index.\n */\nexport function findIndex(arr, func) {\n var index;\n var found = !arr.every(function (el, idx) {\n index = idx;\n return !func(el, idx, arr);\n });\n return found ? index : -1;\n}\n/**\n * @param {Array<*>} arr The array to test.\n * @param {Function=} opt_func Comparison function.\n * @param {boolean=} opt_strict Strictly sorted (default false).\n * @return {boolean} Return index.\n */\nexport function isSorted(arr, opt_func, opt_strict) {\n var compare = opt_func || numberSafeCompareFunction;\n return arr.every(function (currentVal, index) {\n if (index === 0) {\n return true;\n }\n var res = compare(arr[index - 1], currentVal);\n return !(res > 0 || opt_strict && res === 0);\n });\n}\n//# sourceMappingURL=array.js.map","/**\n * @module ol/functions\n */\nimport { equals as arrayEquals } from './array.js';\n/**\n * Always returns true.\n * @returns {boolean} true.\n */\nexport function TRUE() {\n return true;\n}\n/**\n * Always returns false.\n * @returns {boolean} false.\n */\nexport function FALSE() {\n return false;\n}\n/**\n * A reusable function, used e.g. as a default for callbacks.\n *\n * @return {void} Nothing.\n */\nexport function VOID() { }\n/**\n * Wrap a function in another function that remembers the last return. If the\n * returned function is called twice in a row with the same arguments and the same\n * this object, it will return the value from the first call in the second call.\n *\n * @param {function(...any): ReturnType} fn The function to memoize.\n * @return {function(...any): ReturnType} The memoized function.\n * @template ReturnType\n */\nexport function memoizeOne(fn) {\n var called = false;\n /** @type {ReturnType} */\n var lastResult;\n /** @type {Array} */\n var lastArgs;\n var lastThis;\n return function () {\n var nextArgs = Array.prototype.slice.call(arguments);\n if (!called || this !== lastThis || !arrayEquals(nextArgs, lastArgs)) {\n called = true;\n lastThis = this;\n lastArgs = nextArgs;\n lastResult = fn.apply(this, arguments);\n }\n return lastResult;\n };\n}\n//# sourceMappingURL=functions.js.map","/**\n * @module ol/events/Event\n */\n/**\n * @classdesc\n * Stripped down implementation of the W3C DOM Level 2 Event interface.\n * See https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface.\n *\n * This implementation only provides `type` and `target` properties, and\n * `stopPropagation` and `preventDefault` methods. It is meant as base class\n * for higher level events defined in the library, and works with\n * {@link module:ol/events/Target~Target}.\n */\nvar BaseEvent = /** @class */ (function () {\n /**\n * @param {string} type Type.\n */\n function BaseEvent(type) {\n /**\n * @type {boolean}\n */\n this.propagationStopped;\n /**\n * The event type.\n * @type {string}\n * @api\n */\n this.type = type;\n /**\n * The event target.\n * @type {Object}\n * @api\n */\n this.target = null;\n }\n /**\n * Stop event propagation.\n * @api\n */\n BaseEvent.prototype.preventDefault = function () {\n this.propagationStopped = true;\n };\n /**\n * Stop event propagation.\n * @api\n */\n BaseEvent.prototype.stopPropagation = function () {\n this.propagationStopped = true;\n };\n return BaseEvent;\n}());\n/**\n * @param {Event|import(\"./Event.js\").default} evt Event\n */\nexport function stopPropagation(evt) {\n evt.stopPropagation();\n}\n/**\n * @param {Event|import(\"./Event.js\").default} evt Event\n */\nexport function preventDefault(evt) {\n evt.preventDefault();\n}\nexport default BaseEvent;\n//# sourceMappingURL=Event.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/events/Target\n */\nimport Disposable from '../Disposable.js';\nimport { VOID } from '../functions.js';\nimport Event from './Event.js';\nimport { clear } from '../obj.js';\n/**\n * @typedef {EventTarget|Target} EventTargetLike\n */\n/**\n * @classdesc\n * A simplified implementation of the W3C DOM Level 2 EventTarget interface.\n * See https://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-EventTarget.\n *\n * There are two important simplifications compared to the specification:\n *\n * 1. The handling of `useCapture` in `addEventListener` and\n * `removeEventListener`. There is no real capture model.\n * 2. The handling of `stopPropagation` and `preventDefault` on `dispatchEvent`.\n * There is no event target hierarchy. When a listener calls\n * `stopPropagation` or `preventDefault` on an event object, it means that no\n * more listeners after this one will be called. Same as when the listener\n * returns false.\n */\nvar Target = /** @class */ (function (_super) {\n __extends(Target, _super);\n /**\n * @param {*=} opt_target Default event target for dispatched events.\n */\n function Target(opt_target) {\n var _this = _super.call(this) || this;\n /**\n * @private\n * @type {*}\n */\n _this.eventTarget_ = opt_target;\n /**\n * @private\n * @type {!Object}\n */\n _this.pendingRemovals_ = {};\n /**\n * @private\n * @type {!Object}\n */\n _this.dispatching_ = {};\n /**\n * @private\n * @type {!Object>}\n */\n _this.listeners_ = {};\n return _this;\n }\n /**\n * @param {string} type Type.\n * @param {import(\"../events.js\").ListenerFunction} listener Listener.\n */\n Target.prototype.addEventListener = function (type, listener) {\n if (!type || !listener) {\n return;\n }\n var listeners = this.listeners_[type];\n if (!listeners) {\n listeners = [];\n this.listeners_[type] = listeners;\n }\n if (listeners.indexOf(listener) === -1) {\n listeners.push(listener);\n }\n };\n /**\n * Dispatches an event and calls all listeners listening for events\n * of this type. The event parameter can either be a string or an\n * Object with a `type` property.\n *\n * @param {{type: string,\n * target: (EventTargetLike|undefined),\n * propagationStopped: (boolean|undefined)}|\n * import(\"./Event.js\").default|string} event Event object.\n * @return {boolean|undefined} `false` if anyone called preventDefault on the\n * event object or if any of the listeners returned false.\n * @api\n */\n Target.prototype.dispatchEvent = function (event) {\n var evt = typeof event === 'string' ? new Event(event) : event;\n var type = evt.type;\n if (!evt.target) {\n evt.target = this.eventTarget_ || this;\n }\n var listeners = this.listeners_[type];\n var propagate;\n if (listeners) {\n if (!(type in this.dispatching_)) {\n this.dispatching_[type] = 0;\n this.pendingRemovals_[type] = 0;\n }\n ++this.dispatching_[type];\n for (var i = 0, ii = listeners.length; i < ii; ++i) {\n if (listeners[i].call(this, evt) === false || evt.propagationStopped) {\n propagate = false;\n break;\n }\n }\n --this.dispatching_[type];\n if (this.dispatching_[type] === 0) {\n var pendingRemovals = this.pendingRemovals_[type];\n delete this.pendingRemovals_[type];\n while (pendingRemovals--) {\n this.removeEventListener(type, VOID);\n }\n delete this.dispatching_[type];\n }\n return propagate;\n }\n };\n /**\n * @inheritDoc\n */\n Target.prototype.disposeInternal = function () {\n clear(this.listeners_);\n };\n /**\n * Get the listeners for a specified event type. Listeners are returned in the\n * order that they will be called in.\n *\n * @param {string} type Type.\n * @return {Array} Listeners.\n */\n Target.prototype.getListeners = function (type) {\n return this.listeners_[type];\n };\n /**\n * @param {string=} opt_type Type. If not provided,\n * `true` will be returned if this event target has any listeners.\n * @return {boolean} Has listeners.\n */\n Target.prototype.hasListener = function (opt_type) {\n return opt_type ?\n opt_type in this.listeners_ :\n Object.keys(this.listeners_).length > 0;\n };\n /**\n * @param {string} type Type.\n * @param {import(\"../events.js\").ListenerFunction} listener Listener.\n */\n Target.prototype.removeEventListener = function (type, listener) {\n var listeners = this.listeners_[type];\n if (listeners) {\n var index = listeners.indexOf(listener);\n if (index !== -1) {\n if (type in this.pendingRemovals_) {\n // make listener a no-op, and remove later in #dispatchEvent()\n listeners[index] = VOID;\n ++this.pendingRemovals_[type];\n }\n else {\n listeners.splice(index, 1);\n if (listeners.length === 0) {\n delete this.listeners_[type];\n }\n }\n }\n }\n };\n return Target;\n}(Disposable));\nexport default Target;\n//# sourceMappingURL=Target.js.map","/**\n * @module ol/events/EventType\n */\n/**\n * @enum {string}\n * @const\n */\nexport default {\n /**\n * Generic change event. Triggered when the revision counter is increased.\n * @event module:ol/events/Event~BaseEvent#change\n * @api\n */\n CHANGE: 'change',\n /**\n * Generic error event. Triggered when an error occurs.\n * @event module:ol/events/Event~BaseEvent#error\n * @api\n */\n ERROR: 'error',\n BLUR: 'blur',\n CLEAR: 'clear',\n CONTEXTMENU: 'contextmenu',\n CLICK: 'click',\n DBLCLICK: 'dblclick',\n DRAGENTER: 'dragenter',\n DRAGOVER: 'dragover',\n DROP: 'drop',\n FOCUS: 'focus',\n KEYDOWN: 'keydown',\n KEYPRESS: 'keypress',\n LOAD: 'load',\n RESIZE: 'resize',\n WHEEL: 'wheel'\n};\n//# sourceMappingURL=EventType.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/Observable\n */\nimport { listen, unlistenByKey, listenOnce } from './events.js';\nimport EventTarget from './events/Target.js';\nimport EventType from './events/EventType.js';\n/**\n * @classdesc\n * Abstract base class; normally only used for creating subclasses and not\n * instantiated in apps.\n * An event target providing convenient methods for listener registration\n * and unregistration. A generic `change` event is always available through\n * {@link module:ol/Observable~Observable#changed}.\n *\n * @fires import(\"./events/Event.js\").default\n * @api\n */\nvar Observable = /** @class */ (function (_super) {\n __extends(Observable, _super);\n function Observable() {\n var _this = _super.call(this) || this;\n /**\n * @private\n * @type {number}\n */\n _this.revision_ = 0;\n return _this;\n }\n /**\n * Increases the revision counter and dispatches a 'change' event.\n * @api\n */\n Observable.prototype.changed = function () {\n ++this.revision_;\n this.dispatchEvent(EventType.CHANGE);\n };\n /**\n * Get the version number for this object. Each time the object is modified,\n * its version number will be incremented.\n * @return {number} Revision.\n * @api\n */\n Observable.prototype.getRevision = function () {\n return this.revision_;\n };\n /**\n * Listen for a certain type of event.\n * @param {string|Array} type The event type or array of event types.\n * @param {function(?): ?} listener The listener function.\n * @return {import(\"./events.js\").EventsKey|Array} Unique key for the listener. If\n * called with an array of event types as the first argument, the return\n * will be an array of keys.\n * @api\n */\n Observable.prototype.on = function (type, listener) {\n if (Array.isArray(type)) {\n var len = type.length;\n var keys = new Array(len);\n for (var i = 0; i < len; ++i) {\n keys[i] = listen(this, type[i], listener);\n }\n return keys;\n }\n else {\n return listen(this, /** @type {string} */ (type), listener);\n }\n };\n /**\n * Listen once for a certain type of event.\n * @param {string|Array} type The event type or array of event types.\n * @param {function(?): ?} listener The listener function.\n * @return {import(\"./events.js\").EventsKey|Array} Unique key for the listener. If\n * called with an array of event types as the first argument, the return\n * will be an array of keys.\n * @api\n */\n Observable.prototype.once = function (type, listener) {\n if (Array.isArray(type)) {\n var len = type.length;\n var keys = new Array(len);\n for (var i = 0; i < len; ++i) {\n keys[i] = listenOnce(this, type[i], listener);\n }\n return keys;\n }\n else {\n return listenOnce(this, /** @type {string} */ (type), listener);\n }\n };\n /**\n * Unlisten for a certain type of event.\n * @param {string|Array} type The event type or array of event types.\n * @param {function(?): ?} listener The listener function.\n * @api\n */\n Observable.prototype.un = function (type, listener) {\n if (Array.isArray(type)) {\n for (var i = 0, ii = type.length; i < ii; ++i) {\n this.removeEventListener(type[i], listener);\n }\n }\n else {\n this.removeEventListener(type, listener);\n }\n };\n return Observable;\n}(EventTarget));\n/**\n * Removes an event listener using the key returned by `on()` or `once()`.\n * @param {import(\"./events.js\").EventsKey|Array} key The key returned by `on()`\n * or `once()` (or an array of keys).\n * @api\n */\nexport function unByKey(key) {\n if (Array.isArray(key)) {\n for (var i = 0, ii = key.length; i < ii; ++i) {\n unlistenByKey(key[i]);\n }\n }\n else {\n unlistenByKey(/** @type {import(\"./events.js\").EventsKey} */ (key));\n }\n}\nexport default Observable;\n//# sourceMappingURL=Observable.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/Object\n */\nimport { getUid } from './util.js';\nimport ObjectEventType from './ObjectEventType.js';\nimport Observable from './Observable.js';\nimport Event from './events/Event.js';\nimport { assign } from './obj.js';\n/**\n * @classdesc\n * Events emitted by {@link module:ol/Object~BaseObject} instances are instances of this type.\n */\nvar ObjectEvent = /** @class */ (function (_super) {\n __extends(ObjectEvent, _super);\n /**\n * @param {string} type The event type.\n * @param {string} key The property name.\n * @param {*} oldValue The old value for `key`.\n */\n function ObjectEvent(type, key, oldValue) {\n var _this = _super.call(this, type) || this;\n /**\n * The name of the property whose value is changing.\n * @type {string}\n * @api\n */\n _this.key = key;\n /**\n * The old value. To get the new value use `e.target.get(e.key)` where\n * `e` is the event object.\n * @type {*}\n * @api\n */\n _this.oldValue = oldValue;\n return _this;\n }\n return ObjectEvent;\n}(Event));\nexport { ObjectEvent };\n/**\n * @classdesc\n * Abstract base class; normally only used for creating subclasses and not\n * instantiated in apps.\n * Most non-trivial classes inherit from this.\n *\n * This extends {@link module:ol/Observable} with observable\n * properties, where each property is observable as well as the object as a\n * whole.\n *\n * Classes that inherit from this have pre-defined properties, to which you can\n * add your owns. The pre-defined properties are listed in this documentation as\n * 'Observable Properties', and have their own accessors; for example,\n * {@link module:ol/Map~Map} has a `target` property, accessed with\n * `getTarget()` and changed with `setTarget()`. Not all properties are however\n * settable. There are also general-purpose accessors `get()` and `set()`. For\n * example, `get('target')` is equivalent to `getTarget()`.\n *\n * The `set` accessors trigger a change event, and you can monitor this by\n * registering a listener. For example, {@link module:ol/View~View} has a\n * `center` property, so `view.on('change:center', function(evt) {...});` would\n * call the function whenever the value of the center property changes. Within\n * the function, `evt.target` would be the view, so `evt.target.getCenter()`\n * would return the new center.\n *\n * You can add your own observable properties with\n * `object.set('prop', 'value')`, and retrieve that with `object.get('prop')`.\n * You can listen for changes on that property value with\n * `object.on('change:prop', listener)`. You can get a list of all\n * properties with {@link module:ol/Object~BaseObject#getProperties}.\n *\n * Note that the observable properties are separate from standard JS properties.\n * You can, for example, give your map object a title with\n * `map.title='New title'` and with `map.set('title', 'Another title')`. The\n * first will be a `hasOwnProperty`; the second will appear in\n * `getProperties()`. Only the second is observable.\n *\n * Properties can be deleted by using the unset method. E.g.\n * object.unset('foo').\n *\n * @fires ObjectEvent\n * @api\n */\nvar BaseObject = /** @class */ (function (_super) {\n __extends(BaseObject, _super);\n /**\n * @param {Object=} opt_values An object with key-value pairs.\n */\n function BaseObject(opt_values) {\n var _this = _super.call(this) || this;\n // Call {@link module:ol/util~getUid} to ensure that the order of objects' ids is\n // the same as the order in which they were created. This also helps to\n // ensure that object properties are always added in the same order, which\n // helps many JavaScript engines generate faster code.\n getUid(_this);\n /**\n * @private\n * @type {!Object}\n */\n _this.values_ = {};\n if (opt_values !== undefined) {\n _this.setProperties(opt_values);\n }\n return _this;\n }\n /**\n * Gets a value.\n * @param {string} key Key name.\n * @return {*} Value.\n * @api\n */\n BaseObject.prototype.get = function (key) {\n var value;\n if (this.values_.hasOwnProperty(key)) {\n value = this.values_[key];\n }\n return value;\n };\n /**\n * Get a list of object property names.\n * @return {Array} List of property names.\n * @api\n */\n BaseObject.prototype.getKeys = function () {\n return Object.keys(this.values_);\n };\n /**\n * Get an object of all property names and values.\n * @return {Object} Object.\n * @api\n */\n BaseObject.prototype.getProperties = function () {\n return assign({}, this.values_);\n };\n /**\n * @param {string} key Key name.\n * @param {*} oldValue Old value.\n */\n BaseObject.prototype.notify = function (key, oldValue) {\n var eventType;\n eventType = getChangeEventType(key);\n this.dispatchEvent(new ObjectEvent(eventType, key, oldValue));\n eventType = ObjectEventType.PROPERTYCHANGE;\n this.dispatchEvent(new ObjectEvent(eventType, key, oldValue));\n };\n /**\n * Sets a value.\n * @param {string} key Key name.\n * @param {*} value Value.\n * @param {boolean=} opt_silent Update without triggering an event.\n * @api\n */\n BaseObject.prototype.set = function (key, value, opt_silent) {\n if (opt_silent) {\n this.values_[key] = value;\n }\n else {\n var oldValue = this.values_[key];\n this.values_[key] = value;\n if (oldValue !== value) {\n this.notify(key, oldValue);\n }\n }\n };\n /**\n * Sets a collection of key-value pairs. Note that this changes any existing\n * properties and adds new ones (it does not remove any existing properties).\n * @param {Object} values Values.\n * @param {boolean=} opt_silent Update without triggering an event.\n * @api\n */\n BaseObject.prototype.setProperties = function (values, opt_silent) {\n for (var key in values) {\n this.set(key, values[key], opt_silent);\n }\n };\n /**\n * Unsets a property.\n * @param {string} key Key name.\n * @param {boolean=} opt_silent Unset without triggering an event.\n * @api\n */\n BaseObject.prototype.unset = function (key, opt_silent) {\n if (key in this.values_) {\n var oldValue = this.values_[key];\n delete this.values_[key];\n if (!opt_silent) {\n this.notify(key, oldValue);\n }\n }\n };\n return BaseObject;\n}(Observable));\n/**\n * @type {Object}\n */\nvar changeEventTypeCache = {};\n/**\n * @param {string} key Key name.\n * @return {string} Change name.\n */\nexport function getChangeEventType(key) {\n return changeEventTypeCache.hasOwnProperty(key) ?\n changeEventTypeCache[key] :\n (changeEventTypeCache[key] = 'change:' + key);\n}\nexport default BaseObject;\n//# sourceMappingURL=Object.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/Collection\n */\nimport AssertionError from './AssertionError.js';\nimport CollectionEventType from './CollectionEventType.js';\nimport BaseObject from './Object.js';\nimport Event from './events/Event.js';\n/**\n * @enum {string}\n * @private\n */\nvar Property = {\n LENGTH: 'length'\n};\n/**\n * @classdesc\n * Events emitted by {@link module:ol/Collection~Collection} instances are instances of this\n * type.\n */\nvar CollectionEvent = /** @class */ (function (_super) {\n __extends(CollectionEvent, _super);\n /**\n * @param {CollectionEventType} type Type.\n * @param {*=} opt_element Element.\n * @param {number} opt_index The index of the added or removed element.\n */\n function CollectionEvent(type, opt_element, opt_index) {\n var _this = _super.call(this, type) || this;\n /**\n * The element that is added to or removed from the collection.\n * @type {*}\n * @api\n */\n _this.element = opt_element;\n /**\n * The index of the added or removed element.\n * @type {number}\n * @api\n */\n _this.index = opt_index;\n return _this;\n }\n return CollectionEvent;\n}(Event));\nexport { CollectionEvent };\n/**\n * @typedef {Object} Options\n * @property {boolean} [unique=false] Disallow the same item from being added to\n * the collection twice.\n */\n/**\n * @classdesc\n * An expanded version of standard JS Array, adding convenience methods for\n * manipulation. Add and remove changes to the Collection trigger a Collection\n * event. Note that this does not cover changes to the objects _within_ the\n * Collection; they trigger events on the appropriate object, not on the\n * Collection as a whole.\n *\n * @fires CollectionEvent\n *\n * @template T\n * @api\n */\nvar Collection = /** @class */ (function (_super) {\n __extends(Collection, _super);\n /**\n * @param {Array=} opt_array Array.\n * @param {Options=} opt_options Collection options.\n */\n function Collection(opt_array, opt_options) {\n var _this = _super.call(this) || this;\n var options = opt_options || {};\n /**\n * @private\n * @type {boolean}\n */\n _this.unique_ = !!options.unique;\n /**\n * @private\n * @type {!Array}\n */\n _this.array_ = opt_array ? opt_array : [];\n if (_this.unique_) {\n for (var i = 0, ii = _this.array_.length; i < ii; ++i) {\n _this.assertUnique_(_this.array_[i], i);\n }\n }\n _this.updateLength_();\n return _this;\n }\n /**\n * Remove all elements from the collection.\n * @api\n */\n Collection.prototype.clear = function () {\n while (this.getLength() > 0) {\n this.pop();\n }\n };\n /**\n * Add elements to the collection. This pushes each item in the provided array\n * to the end of the collection.\n * @param {!Array} arr Array.\n * @return {Collection} This collection.\n * @api\n */\n Collection.prototype.extend = function (arr) {\n for (var i = 0, ii = arr.length; i < ii; ++i) {\n this.push(arr[i]);\n }\n return this;\n };\n /**\n * Iterate over each element, calling the provided callback.\n * @param {function(T, number, Array): *} f The function to call\n * for every element. This function takes 3 arguments (the element, the\n * index and the array). The return value is ignored.\n * @api\n */\n Collection.prototype.forEach = function (f) {\n var array = this.array_;\n for (var i = 0, ii = array.length; i < ii; ++i) {\n f(array[i], i, array);\n }\n };\n /**\n * Get a reference to the underlying Array object. Warning: if the array\n * is mutated, no events will be dispatched by the collection, and the\n * collection's \"length\" property won't be in sync with the actual length\n * of the array.\n * @return {!Array} Array.\n * @api\n */\n Collection.prototype.getArray = function () {\n return this.array_;\n };\n /**\n * Get the element at the provided index.\n * @param {number} index Index.\n * @return {T} Element.\n * @api\n */\n Collection.prototype.item = function (index) {\n return this.array_[index];\n };\n /**\n * Get the length of this collection.\n * @return {number} The length of the array.\n * @observable\n * @api\n */\n Collection.prototype.getLength = function () {\n return this.get(Property.LENGTH);\n };\n /**\n * Insert an element at the provided index.\n * @param {number} index Index.\n * @param {T} elem Element.\n * @api\n */\n Collection.prototype.insertAt = function (index, elem) {\n if (this.unique_) {\n this.assertUnique_(elem);\n }\n this.array_.splice(index, 0, elem);\n this.updateLength_();\n this.dispatchEvent(new CollectionEvent(CollectionEventType.ADD, elem, index));\n };\n /**\n * Remove the last element of the collection and return it.\n * Return `undefined` if the collection is empty.\n * @return {T|undefined} Element.\n * @api\n */\n Collection.prototype.pop = function () {\n return this.removeAt(this.getLength() - 1);\n };\n /**\n * Insert the provided element at the end of the collection.\n * @param {T} elem Element.\n * @return {number} New length of the collection.\n * @api\n */\n Collection.prototype.push = function (elem) {\n if (this.unique_) {\n this.assertUnique_(elem);\n }\n var n = this.getLength();\n this.insertAt(n, elem);\n return this.getLength();\n };\n /**\n * Remove the first occurrence of an element from the collection.\n * @param {T} elem Element.\n * @return {T|undefined} The removed element or undefined if none found.\n * @api\n */\n Collection.prototype.remove = function (elem) {\n var arr = this.array_;\n for (var i = 0, ii = arr.length; i < ii; ++i) {\n if (arr[i] === elem) {\n return this.removeAt(i);\n }\n }\n return undefined;\n };\n /**\n * Remove the element at the provided index and return it.\n * Return `undefined` if the collection does not contain this index.\n * @param {number} index Index.\n * @return {T|undefined} Value.\n * @api\n */\n Collection.prototype.removeAt = function (index) {\n var prev = this.array_[index];\n this.array_.splice(index, 1);\n this.updateLength_();\n this.dispatchEvent(new CollectionEvent(CollectionEventType.REMOVE, prev, index));\n return prev;\n };\n /**\n * Set the element at the provided index.\n * @param {number} index Index.\n * @param {T} elem Element.\n * @api\n */\n Collection.prototype.setAt = function (index, elem) {\n var n = this.getLength();\n if (index < n) {\n if (this.unique_) {\n this.assertUnique_(elem, index);\n }\n var prev = this.array_[index];\n this.array_[index] = elem;\n this.dispatchEvent(new CollectionEvent(CollectionEventType.REMOVE, prev, index));\n this.dispatchEvent(new CollectionEvent(CollectionEventType.ADD, elem, index));\n }\n else {\n for (var j = n; j < index; ++j) {\n this.insertAt(j, undefined);\n }\n this.insertAt(index, elem);\n }\n };\n /**\n * @private\n */\n Collection.prototype.updateLength_ = function () {\n this.set(Property.LENGTH, this.array_.length);\n };\n /**\n * @private\n * @param {T} elem Element.\n * @param {number=} opt_except Optional index to ignore.\n */\n Collection.prototype.assertUnique_ = function (elem, opt_except) {\n for (var i = 0, ii = this.array_.length; i < ii; ++i) {\n if (this.array_[i] === elem && i !== opt_except) {\n throw new AssertionError(58);\n }\n }\n };\n return Collection;\n}(BaseObject));\nexport default Collection;\n//# sourceMappingURL=Collection.js.map","/**\n * @module ol/asserts\n */\nimport AssertionError from './AssertionError.js';\n/**\n * @param {*} assertion Assertion we expected to be truthy.\n * @param {number} errorCode Error code.\n */\nexport function assert(assertion, errorCode) {\n if (!assertion) {\n throw new AssertionError(errorCode);\n }\n}\n//# sourceMappingURL=asserts.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/Feature\n */\nimport { assert } from './asserts.js';\nimport { listen, unlistenByKey } from './events.js';\nimport EventType from './events/EventType.js';\nimport BaseObject, { getChangeEventType } from './Object.js';\n/**\n * @typedef {typeof Feature|typeof import(\"./render/Feature.js\").default} FeatureClass\n */\n/**\n * @typedef {Feature|import(\"./render/Feature.js\").default} FeatureLike\n */\n/**\n * @classdesc\n * A vector object for geographic features with a geometry and other\n * attribute properties, similar to the features in vector file formats like\n * GeoJSON.\n *\n * Features can be styled individually with `setStyle`; otherwise they use the\n * style of their vector layer.\n *\n * Note that attribute properties are set as {@link module:ol/Object} properties on\n * the feature object, so they are observable, and have get/set accessors.\n *\n * Typically, a feature has a single geometry property. You can set the\n * geometry using the `setGeometry` method and get it with `getGeometry`.\n * It is possible to store more than one geometry on a feature using attribute\n * properties. By default, the geometry used for rendering is identified by\n * the property name `geometry`. If you want to use another geometry property\n * for rendering, use the `setGeometryName` method to change the attribute\n * property associated with the geometry for the feature. For example:\n *\n * ```js\n *\n * import Feature from 'ol/Feature';\n * import Polygon from 'ol/geom/Polygon';\n * import Point from 'ol/geom/Point';\n *\n * var feature = new Feature({\n * geometry: new Polygon(polyCoords),\n * labelPoint: new Point(labelCoords),\n * name: 'My Polygon'\n * });\n *\n * // get the polygon geometry\n * var poly = feature.getGeometry();\n *\n * // Render the feature as a point using the coordinates from labelPoint\n * feature.setGeometryName('labelPoint');\n *\n * // get the point geometry\n * var point = feature.getGeometry();\n * ```\n *\n * @api\n * @template {import(\"./geom/Geometry.js\").default} Geometry\n */\nvar Feature = /** @class */ (function (_super) {\n __extends(Feature, _super);\n /**\n * @param {Geometry|Object=} opt_geometryOrProperties\n * You may pass a Geometry object directly, or an object literal containing\n * properties. If you pass an object literal, you may include a Geometry\n * associated with a `geometry` key.\n */\n function Feature(opt_geometryOrProperties) {\n var _this = _super.call(this) || this;\n /**\n * @private\n * @type {number|string|undefined}\n */\n _this.id_ = undefined;\n /**\n * @type {string}\n * @private\n */\n _this.geometryName_ = 'geometry';\n /**\n * User provided style.\n * @private\n * @type {import(\"./style/Style.js\").StyleLike}\n */\n _this.style_ = null;\n /**\n * @private\n * @type {import(\"./style/Style.js\").StyleFunction|undefined}\n */\n _this.styleFunction_ = undefined;\n /**\n * @private\n * @type {?import(\"./events.js\").EventsKey}\n */\n _this.geometryChangeKey_ = null;\n _this.addEventListener(getChangeEventType(_this.geometryName_), _this.handleGeometryChanged_);\n if (opt_geometryOrProperties) {\n if (typeof /** @type {?} */ (opt_geometryOrProperties).getSimplifiedGeometry === 'function') {\n var geometry = /** @type {Geometry} */ (opt_geometryOrProperties);\n _this.setGeometry(geometry);\n }\n else {\n /** @type {Object} */\n var properties = opt_geometryOrProperties;\n _this.setProperties(properties);\n }\n }\n return _this;\n }\n /**\n * Clone this feature. If the original feature has a geometry it\n * is also cloned. The feature id is not set in the clone.\n * @return {Feature} The clone.\n * @api\n */\n Feature.prototype.clone = function () {\n var clone = new Feature(this.getProperties());\n clone.setGeometryName(this.getGeometryName());\n var geometry = this.getGeometry();\n if (geometry) {\n clone.setGeometry(geometry.clone());\n }\n var style = this.getStyle();\n if (style) {\n clone.setStyle(style);\n }\n return clone;\n };\n /**\n * Get the feature's default geometry. A feature may have any number of named\n * geometries. The \"default\" geometry (the one that is rendered by default) is\n * set when calling {@link module:ol/Feature~Feature#setGeometry}.\n * @return {Geometry|undefined} The default geometry for the feature.\n * @api\n * @observable\n */\n Feature.prototype.getGeometry = function () {\n return (\n /** @type {Geometry|undefined} */ (this.get(this.geometryName_)));\n };\n /**\n * Get the feature identifier. This is a stable identifier for the feature and\n * is either set when reading data from a remote source or set explicitly by\n * calling {@link module:ol/Feature~Feature#setId}.\n * @return {number|string|undefined} Id.\n * @api\n */\n Feature.prototype.getId = function () {\n return this.id_;\n };\n /**\n * Get the name of the feature's default geometry. By default, the default\n * geometry is named `geometry`.\n * @return {string} Get the property name associated with the default geometry\n * for this feature.\n * @api\n */\n Feature.prototype.getGeometryName = function () {\n return this.geometryName_;\n };\n /**\n * Get the feature's style. Will return what was provided to the\n * {@link module:ol/Feature~Feature#setStyle} method.\n * @return {import(\"./style/Style.js\").StyleLike} The feature style.\n * @api\n */\n Feature.prototype.getStyle = function () {\n return this.style_;\n };\n /**\n * Get the feature's style function.\n * @return {import(\"./style/Style.js\").StyleFunction|undefined} Return a function\n * representing the current style of this feature.\n * @api\n */\n Feature.prototype.getStyleFunction = function () {\n return this.styleFunction_;\n };\n /**\n * @private\n */\n Feature.prototype.handleGeometryChange_ = function () {\n this.changed();\n };\n /**\n * @private\n */\n Feature.prototype.handleGeometryChanged_ = function () {\n if (this.geometryChangeKey_) {\n unlistenByKey(this.geometryChangeKey_);\n this.geometryChangeKey_ = null;\n }\n var geometry = this.getGeometry();\n if (geometry) {\n this.geometryChangeKey_ = listen(geometry, EventType.CHANGE, this.handleGeometryChange_, this);\n }\n this.changed();\n };\n /**\n * Set the default geometry for the feature. This will update the property\n * with the name returned by {@link module:ol/Feature~Feature#getGeometryName}.\n * @param {Geometry|undefined} geometry The new geometry.\n * @api\n * @observable\n */\n Feature.prototype.setGeometry = function (geometry) {\n this.set(this.geometryName_, geometry);\n };\n /**\n * Set the style for the feature. This can be a single style object, an array\n * of styles, or a function that takes a resolution and returns an array of\n * styles. If it is `null` the feature has no style (a `null` style).\n * @param {import(\"./style/Style.js\").StyleLike} style Style for this feature.\n * @api\n * @fires module:ol/events/Event~BaseEvent#event:change\n */\n Feature.prototype.setStyle = function (style) {\n this.style_ = style;\n this.styleFunction_ = !style ? undefined : createStyleFunction(style);\n this.changed();\n };\n /**\n * Set the feature id. The feature id is considered stable and may be used when\n * requesting features or comparing identifiers returned from a remote source.\n * The feature id can be used with the\n * {@link module:ol/source/Vector~VectorSource#getFeatureById} method.\n * @param {number|string|undefined} id The feature id.\n * @api\n * @fires module:ol/events/Event~BaseEvent#event:change\n */\n Feature.prototype.setId = function (id) {\n this.id_ = id;\n this.changed();\n };\n /**\n * Set the property name to be used when getting the feature's default geometry.\n * When calling {@link module:ol/Feature~Feature#getGeometry}, the value of the property with\n * this name will be returned.\n * @param {string} name The property name of the default geometry.\n * @api\n */\n Feature.prototype.setGeometryName = function (name) {\n this.removeEventListener(getChangeEventType(this.geometryName_), this.handleGeometryChanged_);\n this.geometryName_ = name;\n this.addEventListener(getChangeEventType(this.geometryName_), this.handleGeometryChanged_);\n this.handleGeometryChanged_();\n };\n return Feature;\n}(BaseObject));\n/**\n * Convert the provided object into a feature style function. Functions passed\n * through unchanged. Arrays of Style or single style objects wrapped\n * in a new feature style function.\n * @param {!import(\"./style/Style.js\").StyleFunction|!Array|!import(\"./style/Style.js\").default} obj\n * A feature style function, a single style, or an array of styles.\n * @return {import(\"./style/Style.js\").StyleFunction} A style function.\n */\nexport function createStyleFunction(obj) {\n if (typeof obj === 'function') {\n return obj;\n }\n else {\n /**\n * @type {Array}\n */\n var styles_1;\n if (Array.isArray(obj)) {\n styles_1 = obj;\n }\n else {\n assert(typeof /** @type {?} */ (obj).getZIndex === 'function', 41); // Expected an `import(\"./style/Style.js\").Style` or an array of `import(\"./style/Style.js\").Style`\n var style = /** @type {import(\"./style/Style.js\").default} */ (obj);\n styles_1 = [style];\n }\n return function () {\n return styles_1;\n };\n }\n}\nexport default Feature;\n//# sourceMappingURL=Feature.js.map","/**\n * @module ol/extent/Corner\n */\n/**\n * Extent corner.\n * @enum {string}\n */\nexport default {\n BOTTOM_LEFT: 'bottom-left',\n BOTTOM_RIGHT: 'bottom-right',\n TOP_LEFT: 'top-left',\n TOP_RIGHT: 'top-right'\n};\n//# sourceMappingURL=Corner.js.map","/**\n * @module ol/extent/Relationship\n */\n/**\n * Relationship to an extent.\n * @enum {number}\n */\nexport default {\n UNKNOWN: 0,\n INTERSECTING: 1,\n ABOVE: 2,\n RIGHT: 4,\n BELOW: 8,\n LEFT: 16\n};\n//# sourceMappingURL=Relationship.js.map","/**\n * @module ol/extent\n */\nimport { assert } from './asserts.js';\nimport Corner from './extent/Corner.js';\nimport Relationship from './extent/Relationship.js';\n/**\n * An array of numbers representing an extent: `[minx, miny, maxx, maxy]`.\n * @typedef {Array} Extent\n * @api\n */\n/**\n * Build an extent that includes all given coordinates.\n *\n * @param {Array} coordinates Coordinates.\n * @return {Extent} Bounding extent.\n * @api\n */\nexport function boundingExtent(coordinates) {\n var extent = createEmpty();\n for (var i = 0, ii = coordinates.length; i < ii; ++i) {\n extendCoordinate(extent, coordinates[i]);\n }\n return extent;\n}\n/**\n * @param {Array} xs Xs.\n * @param {Array} ys Ys.\n * @param {Extent=} opt_extent Destination extent.\n * @private\n * @return {Extent} Extent.\n */\nfunction _boundingExtentXYs(xs, ys, opt_extent) {\n var minX = Math.min.apply(null, xs);\n var minY = Math.min.apply(null, ys);\n var maxX = Math.max.apply(null, xs);\n var maxY = Math.max.apply(null, ys);\n return createOrUpdate(minX, minY, maxX, maxY, opt_extent);\n}\n/**\n * Return extent increased by the provided value.\n * @param {Extent} extent Extent.\n * @param {number} value The amount by which the extent should be buffered.\n * @param {Extent=} opt_extent Extent.\n * @return {Extent} Extent.\n * @api\n */\nexport function buffer(extent, value, opt_extent) {\n if (opt_extent) {\n opt_extent[0] = extent[0] - value;\n opt_extent[1] = extent[1] - value;\n opt_extent[2] = extent[2] + value;\n opt_extent[3] = extent[3] + value;\n return opt_extent;\n }\n else {\n return [\n extent[0] - value,\n extent[1] - value,\n extent[2] + value,\n extent[3] + value\n ];\n }\n}\n/**\n * Creates a clone of an extent.\n *\n * @param {Extent} extent Extent to clone.\n * @param {Extent=} opt_extent Extent.\n * @return {Extent} The clone.\n */\nexport function clone(extent, opt_extent) {\n if (opt_extent) {\n opt_extent[0] = extent[0];\n opt_extent[1] = extent[1];\n opt_extent[2] = extent[2];\n opt_extent[3] = extent[3];\n return opt_extent;\n }\n else {\n return extent.slice();\n }\n}\n/**\n * @param {Extent} extent Extent.\n * @param {number} x X.\n * @param {number} y Y.\n * @return {number} Closest squared distance.\n */\nexport function closestSquaredDistanceXY(extent, x, y) {\n var dx, dy;\n if (x < extent[0]) {\n dx = extent[0] - x;\n }\n else if (extent[2] < x) {\n dx = x - extent[2];\n }\n else {\n dx = 0;\n }\n if (y < extent[1]) {\n dy = extent[1] - y;\n }\n else if (extent[3] < y) {\n dy = y - extent[3];\n }\n else {\n dy = 0;\n }\n return dx * dx + dy * dy;\n}\n/**\n * Check if the passed coordinate is contained or on the edge of the extent.\n *\n * @param {Extent} extent Extent.\n * @param {import(\"./coordinate.js\").Coordinate} coordinate Coordinate.\n * @return {boolean} The coordinate is contained in the extent.\n * @api\n */\nexport function containsCoordinate(extent, coordinate) {\n return containsXY(extent, coordinate[0], coordinate[1]);\n}\n/**\n * Check if one extent contains another.\n *\n * An extent is deemed contained if it lies completely within the other extent,\n * including if they share one or more edges.\n *\n * @param {Extent} extent1 Extent 1.\n * @param {Extent} extent2 Extent 2.\n * @return {boolean} The second extent is contained by or on the edge of the\n * first.\n * @api\n */\nexport function containsExtent(extent1, extent2) {\n return extent1[0] <= extent2[0] && extent2[2] <= extent1[2] &&\n extent1[1] <= extent2[1] && extent2[3] <= extent1[3];\n}\n/**\n * Check if the passed coordinate is contained or on the edge of the extent.\n *\n * @param {Extent} extent Extent.\n * @param {number} x X coordinate.\n * @param {number} y Y coordinate.\n * @return {boolean} The x, y values are contained in the extent.\n * @api\n */\nexport function containsXY(extent, x, y) {\n return extent[0] <= x && x <= extent[2] && extent[1] <= y && y <= extent[3];\n}\n/**\n * Get the relationship between a coordinate and extent.\n * @param {Extent} extent The extent.\n * @param {import(\"./coordinate.js\").Coordinate} coordinate The coordinate.\n * @return {Relationship} The relationship (bitwise compare with\n * import(\"./extent/Relationship.js\").Relationship).\n */\nexport function coordinateRelationship(extent, coordinate) {\n var minX = extent[0];\n var minY = extent[1];\n var maxX = extent[2];\n var maxY = extent[3];\n var x = coordinate[0];\n var y = coordinate[1];\n var relationship = Relationship.UNKNOWN;\n if (x < minX) {\n relationship = relationship | Relationship.LEFT;\n }\n else if (x > maxX) {\n relationship = relationship | Relationship.RIGHT;\n }\n if (y < minY) {\n relationship = relationship | Relationship.BELOW;\n }\n else if (y > maxY) {\n relationship = relationship | Relationship.ABOVE;\n }\n if (relationship === Relationship.UNKNOWN) {\n relationship = Relationship.INTERSECTING;\n }\n return relationship;\n}\n/**\n * Create an empty extent.\n * @return {Extent} Empty extent.\n * @api\n */\nexport function createEmpty() {\n return [Infinity, Infinity, -Infinity, -Infinity];\n}\n/**\n * Create a new extent or update the provided extent.\n * @param {number} minX Minimum X.\n * @param {number} minY Minimum Y.\n * @param {number} maxX Maximum X.\n * @param {number} maxY Maximum Y.\n * @param {Extent=} opt_extent Destination extent.\n * @return {Extent} Extent.\n */\nexport function createOrUpdate(minX, minY, maxX, maxY, opt_extent) {\n if (opt_extent) {\n opt_extent[0] = minX;\n opt_extent[1] = minY;\n opt_extent[2] = maxX;\n opt_extent[3] = maxY;\n return opt_extent;\n }\n else {\n return [minX, minY, maxX, maxY];\n }\n}\n/**\n * Create a new empty extent or make the provided one empty.\n * @param {Extent=} opt_extent Extent.\n * @return {Extent} Extent.\n */\nexport function createOrUpdateEmpty(opt_extent) {\n return createOrUpdate(Infinity, Infinity, -Infinity, -Infinity, opt_extent);\n}\n/**\n * @param {import(\"./coordinate.js\").Coordinate} coordinate Coordinate.\n * @param {Extent=} opt_extent Extent.\n * @return {Extent} Extent.\n */\nexport function createOrUpdateFromCoordinate(coordinate, opt_extent) {\n var x = coordinate[0];\n var y = coordinate[1];\n return createOrUpdate(x, y, x, y, opt_extent);\n}\n/**\n * @param {Array} coordinates Coordinates.\n * @param {Extent=} opt_extent Extent.\n * @return {Extent} Extent.\n */\nexport function createOrUpdateFromCoordinates(coordinates, opt_extent) {\n var extent = createOrUpdateEmpty(opt_extent);\n return extendCoordinates(extent, coordinates);\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {Extent=} opt_extent Extent.\n * @return {Extent} Extent.\n */\nexport function createOrUpdateFromFlatCoordinates(flatCoordinates, offset, end, stride, opt_extent) {\n var extent = createOrUpdateEmpty(opt_extent);\n return extendFlatCoordinates(extent, flatCoordinates, offset, end, stride);\n}\n/**\n * @param {Array>} rings Rings.\n * @param {Extent=} opt_extent Extent.\n * @return {Extent} Extent.\n */\nexport function createOrUpdateFromRings(rings, opt_extent) {\n var extent = createOrUpdateEmpty(opt_extent);\n return extendRings(extent, rings);\n}\n/**\n * Determine if two extents are equivalent.\n * @param {Extent} extent1 Extent 1.\n * @param {Extent} extent2 Extent 2.\n * @return {boolean} The two extents are equivalent.\n * @api\n */\nexport function equals(extent1, extent2) {\n return extent1[0] == extent2[0] && extent1[2] == extent2[2] &&\n extent1[1] == extent2[1] && extent1[3] == extent2[3];\n}\n/**\n * Modify an extent to include another extent.\n * @param {Extent} extent1 The extent to be modified.\n * @param {Extent} extent2 The extent that will be included in the first.\n * @return {Extent} A reference to the first (extended) extent.\n * @api\n */\nexport function extend(extent1, extent2) {\n if (extent2[0] < extent1[0]) {\n extent1[0] = extent2[0];\n }\n if (extent2[2] > extent1[2]) {\n extent1[2] = extent2[2];\n }\n if (extent2[1] < extent1[1]) {\n extent1[1] = extent2[1];\n }\n if (extent2[3] > extent1[3]) {\n extent1[3] = extent2[3];\n }\n return extent1;\n}\n/**\n * @param {Extent} extent Extent.\n * @param {import(\"./coordinate.js\").Coordinate} coordinate Coordinate.\n */\nexport function extendCoordinate(extent, coordinate) {\n if (coordinate[0] < extent[0]) {\n extent[0] = coordinate[0];\n }\n if (coordinate[0] > extent[2]) {\n extent[2] = coordinate[0];\n }\n if (coordinate[1] < extent[1]) {\n extent[1] = coordinate[1];\n }\n if (coordinate[1] > extent[3]) {\n extent[3] = coordinate[1];\n }\n}\n/**\n * @param {Extent} extent Extent.\n * @param {Array} coordinates Coordinates.\n * @return {Extent} Extent.\n */\nexport function extendCoordinates(extent, coordinates) {\n for (var i = 0, ii = coordinates.length; i < ii; ++i) {\n extendCoordinate(extent, coordinates[i]);\n }\n return extent;\n}\n/**\n * @param {Extent} extent Extent.\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @return {Extent} Extent.\n */\nexport function extendFlatCoordinates(extent, flatCoordinates, offset, end, stride) {\n for (; offset < end; offset += stride) {\n extendXY(extent, flatCoordinates[offset], flatCoordinates[offset + 1]);\n }\n return extent;\n}\n/**\n * @param {Extent} extent Extent.\n * @param {Array>} rings Rings.\n * @return {Extent} Extent.\n */\nexport function extendRings(extent, rings) {\n for (var i = 0, ii = rings.length; i < ii; ++i) {\n extendCoordinates(extent, rings[i]);\n }\n return extent;\n}\n/**\n * @param {Extent} extent Extent.\n * @param {number} x X.\n * @param {number} y Y.\n */\nexport function extendXY(extent, x, y) {\n extent[0] = Math.min(extent[0], x);\n extent[1] = Math.min(extent[1], y);\n extent[2] = Math.max(extent[2], x);\n extent[3] = Math.max(extent[3], y);\n}\n/**\n * This function calls `callback` for each corner of the extent. If the\n * callback returns a truthy value the function returns that value\n * immediately. Otherwise the function returns `false`.\n * @param {Extent} extent Extent.\n * @param {function(import(\"./coordinate.js\").Coordinate): S} callback Callback.\n * @return {S|boolean} Value.\n * @template S\n */\nexport function forEachCorner(extent, callback) {\n var val;\n val = callback(getBottomLeft(extent));\n if (val) {\n return val;\n }\n val = callback(getBottomRight(extent));\n if (val) {\n return val;\n }\n val = callback(getTopRight(extent));\n if (val) {\n return val;\n }\n val = callback(getTopLeft(extent));\n if (val) {\n return val;\n }\n return false;\n}\n/**\n * Get the size of an extent.\n * @param {Extent} extent Extent.\n * @return {number} Area.\n * @api\n */\nexport function getArea(extent) {\n var area = 0;\n if (!isEmpty(extent)) {\n area = getWidth(extent) * getHeight(extent);\n }\n return area;\n}\n/**\n * Get the bottom left coordinate of an extent.\n * @param {Extent} extent Extent.\n * @return {import(\"./coordinate.js\").Coordinate} Bottom left coordinate.\n * @api\n */\nexport function getBottomLeft(extent) {\n return [extent[0], extent[1]];\n}\n/**\n * Get the bottom right coordinate of an extent.\n * @param {Extent} extent Extent.\n * @return {import(\"./coordinate.js\").Coordinate} Bottom right coordinate.\n * @api\n */\nexport function getBottomRight(extent) {\n return [extent[2], extent[1]];\n}\n/**\n * Get the center coordinate of an extent.\n * @param {Extent} extent Extent.\n * @return {import(\"./coordinate.js\").Coordinate} Center.\n * @api\n */\nexport function getCenter(extent) {\n return [(extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2];\n}\n/**\n * Get a corner coordinate of an extent.\n * @param {Extent} extent Extent.\n * @param {Corner} corner Corner.\n * @return {import(\"./coordinate.js\").Coordinate} Corner coordinate.\n */\nexport function getCorner(extent, corner) {\n var coordinate;\n if (corner === Corner.BOTTOM_LEFT) {\n coordinate = getBottomLeft(extent);\n }\n else if (corner === Corner.BOTTOM_RIGHT) {\n coordinate = getBottomRight(extent);\n }\n else if (corner === Corner.TOP_LEFT) {\n coordinate = getTopLeft(extent);\n }\n else if (corner === Corner.TOP_RIGHT) {\n coordinate = getTopRight(extent);\n }\n else {\n assert(false, 13); // Invalid corner\n }\n return coordinate;\n}\n/**\n * @param {Extent} extent1 Extent 1.\n * @param {Extent} extent2 Extent 2.\n * @return {number} Enlarged area.\n */\nexport function getEnlargedArea(extent1, extent2) {\n var minX = Math.min(extent1[0], extent2[0]);\n var minY = Math.min(extent1[1], extent2[1]);\n var maxX = Math.max(extent1[2], extent2[2]);\n var maxY = Math.max(extent1[3], extent2[3]);\n return (maxX - minX) * (maxY - minY);\n}\n/**\n * @param {import(\"./coordinate.js\").Coordinate} center Center.\n * @param {number} resolution Resolution.\n * @param {number} rotation Rotation.\n * @param {import(\"./size.js\").Size} size Size.\n * @param {Extent=} opt_extent Destination extent.\n * @return {Extent} Extent.\n */\nexport function getForViewAndSize(center, resolution, rotation, size, opt_extent) {\n var dx = resolution * size[0] / 2;\n var dy = resolution * size[1] / 2;\n var cosRotation = Math.cos(rotation);\n var sinRotation = Math.sin(rotation);\n var xCos = dx * cosRotation;\n var xSin = dx * sinRotation;\n var yCos = dy * cosRotation;\n var ySin = dy * sinRotation;\n var x = center[0];\n var y = center[1];\n var x0 = x - xCos + ySin;\n var x1 = x - xCos - ySin;\n var x2 = x + xCos - ySin;\n var x3 = x + xCos + ySin;\n var y0 = y - xSin - yCos;\n var y1 = y - xSin + yCos;\n var y2 = y + xSin + yCos;\n var y3 = y + xSin - yCos;\n return createOrUpdate(Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3), Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3), opt_extent);\n}\n/**\n * Get the height of an extent.\n * @param {Extent} extent Extent.\n * @return {number} Height.\n * @api\n */\nexport function getHeight(extent) {\n return extent[3] - extent[1];\n}\n/**\n * @param {Extent} extent1 Extent 1.\n * @param {Extent} extent2 Extent 2.\n * @return {number} Intersection area.\n */\nexport function getIntersectionArea(extent1, extent2) {\n var intersection = getIntersection(extent1, extent2);\n return getArea(intersection);\n}\n/**\n * Get the intersection of two extents.\n * @param {Extent} extent1 Extent 1.\n * @param {Extent} extent2 Extent 2.\n * @param {Extent=} opt_extent Optional extent to populate with intersection.\n * @return {Extent} Intersecting extent.\n * @api\n */\nexport function getIntersection(extent1, extent2, opt_extent) {\n var intersection = opt_extent ? opt_extent : createEmpty();\n if (intersects(extent1, extent2)) {\n if (extent1[0] > extent2[0]) {\n intersection[0] = extent1[0];\n }\n else {\n intersection[0] = extent2[0];\n }\n if (extent1[1] > extent2[1]) {\n intersection[1] = extent1[1];\n }\n else {\n intersection[1] = extent2[1];\n }\n if (extent1[2] < extent2[2]) {\n intersection[2] = extent1[2];\n }\n else {\n intersection[2] = extent2[2];\n }\n if (extent1[3] < extent2[3]) {\n intersection[3] = extent1[3];\n }\n else {\n intersection[3] = extent2[3];\n }\n }\n else {\n createOrUpdateEmpty(intersection);\n }\n return intersection;\n}\n/**\n * @param {Extent} extent Extent.\n * @return {number} Margin.\n */\nexport function getMargin(extent) {\n return getWidth(extent) + getHeight(extent);\n}\n/**\n * Get the size (width, height) of an extent.\n * @param {Extent} extent The extent.\n * @return {import(\"./size.js\").Size} The extent size.\n * @api\n */\nexport function getSize(extent) {\n return [extent[2] - extent[0], extent[3] - extent[1]];\n}\n/**\n * Get the top left coordinate of an extent.\n * @param {Extent} extent Extent.\n * @return {import(\"./coordinate.js\").Coordinate} Top left coordinate.\n * @api\n */\nexport function getTopLeft(extent) {\n return [extent[0], extent[3]];\n}\n/**\n * Get the top right coordinate of an extent.\n * @param {Extent} extent Extent.\n * @return {import(\"./coordinate.js\").Coordinate} Top right coordinate.\n * @api\n */\nexport function getTopRight(extent) {\n return [extent[2], extent[3]];\n}\n/**\n * Get the width of an extent.\n * @param {Extent} extent Extent.\n * @return {number} Width.\n * @api\n */\nexport function getWidth(extent) {\n return extent[2] - extent[0];\n}\n/**\n * Determine if one extent intersects another.\n * @param {Extent} extent1 Extent 1.\n * @param {Extent} extent2 Extent.\n * @return {boolean} The two extents intersect.\n * @api\n */\nexport function intersects(extent1, extent2) {\n return extent1[0] <= extent2[2] &&\n extent1[2] >= extent2[0] &&\n extent1[1] <= extent2[3] &&\n extent1[3] >= extent2[1];\n}\n/**\n * Determine if an extent is empty.\n * @param {Extent} extent Extent.\n * @return {boolean} Is empty.\n * @api\n */\nexport function isEmpty(extent) {\n return extent[2] < extent[0] || extent[3] < extent[1];\n}\n/**\n * @param {Extent} extent Extent.\n * @param {Extent=} opt_extent Extent.\n * @return {Extent} Extent.\n */\nexport function returnOrUpdate(extent, opt_extent) {\n if (opt_extent) {\n opt_extent[0] = extent[0];\n opt_extent[1] = extent[1];\n opt_extent[2] = extent[2];\n opt_extent[3] = extent[3];\n return opt_extent;\n }\n else {\n return extent;\n }\n}\n/**\n * @param {Extent} extent Extent.\n * @param {number} value Value.\n */\nexport function scaleFromCenter(extent, value) {\n var deltaX = ((extent[2] - extent[0]) / 2) * (value - 1);\n var deltaY = ((extent[3] - extent[1]) / 2) * (value - 1);\n extent[0] -= deltaX;\n extent[2] += deltaX;\n extent[1] -= deltaY;\n extent[3] += deltaY;\n}\n/**\n * Determine if the segment between two coordinates intersects (crosses,\n * touches, or is contained by) the provided extent.\n * @param {Extent} extent The extent.\n * @param {import(\"./coordinate.js\").Coordinate} start Segment start coordinate.\n * @param {import(\"./coordinate.js\").Coordinate} end Segment end coordinate.\n * @return {boolean} The segment intersects the extent.\n */\nexport function intersectsSegment(extent, start, end) {\n var intersects = false;\n var startRel = coordinateRelationship(extent, start);\n var endRel = coordinateRelationship(extent, end);\n if (startRel === Relationship.INTERSECTING ||\n endRel === Relationship.INTERSECTING) {\n intersects = true;\n }\n else {\n var minX = extent[0];\n var minY = extent[1];\n var maxX = extent[2];\n var maxY = extent[3];\n var startX = start[0];\n var startY = start[1];\n var endX = end[0];\n var endY = end[1];\n var slope = (endY - startY) / (endX - startX);\n var x = void 0, y = void 0;\n if (!!(endRel & Relationship.ABOVE) &&\n !(startRel & Relationship.ABOVE)) {\n // potentially intersects top\n x = endX - ((endY - maxY) / slope);\n intersects = x >= minX && x <= maxX;\n }\n if (!intersects && !!(endRel & Relationship.RIGHT) &&\n !(startRel & Relationship.RIGHT)) {\n // potentially intersects right\n y = endY - ((endX - maxX) * slope);\n intersects = y >= minY && y <= maxY;\n }\n if (!intersects && !!(endRel & Relationship.BELOW) &&\n !(startRel & Relationship.BELOW)) {\n // potentially intersects bottom\n x = endX - ((endY - minY) / slope);\n intersects = x >= minX && x <= maxX;\n }\n if (!intersects && !!(endRel & Relationship.LEFT) &&\n !(startRel & Relationship.LEFT)) {\n // potentially intersects left\n y = endY - ((endX - minX) * slope);\n intersects = y >= minY && y <= maxY;\n }\n }\n return intersects;\n}\n/**\n * Apply a transform function to the extent.\n * @param {Extent} extent Extent.\n * @param {import(\"./proj.js\").TransformFunction} transformFn Transform function.\n * Called with `[minX, minY, maxX, maxY]` extent coordinates.\n * @param {Extent=} opt_extent Destination extent.\n * @return {Extent} Extent.\n * @api\n */\nexport function applyTransform(extent, transformFn, opt_extent) {\n var coordinates = [\n extent[0], extent[1],\n extent[0], extent[3],\n extent[2], extent[1],\n extent[2], extent[3]\n ];\n transformFn(coordinates, coordinates, 2);\n var xs = [coordinates[0], coordinates[2], coordinates[4], coordinates[6]];\n var ys = [coordinates[1], coordinates[3], coordinates[5], coordinates[7]];\n return _boundingExtentXYs(xs, ys, opt_extent);\n}\n//# sourceMappingURL=extent.js.map","/**\n * @module ol/geom/GeometryLayout\n */\n/**\n * The coordinate layout for geometries, indicating whether a 3rd or 4th z ('Z')\n * or measure ('M') coordinate is available. Supported values are `'XY'`,\n * `'XYZ'`, `'XYM'`, `'XYZM'`.\n * @enum {string}\n */\nexport default {\n XY: 'XY',\n XYZ: 'XYZ',\n XYM: 'XYM',\n XYZM: 'XYZM'\n};\n//# sourceMappingURL=GeometryLayout.js.map","/**\n * @module ol/geom/GeometryType\n */\n/**\n * The geometry type. One of `'Point'`, `'LineString'`, `'LinearRing'`,\n * `'Polygon'`, `'MultiPoint'`, `'MultiLineString'`, `'MultiPolygon'`,\n * `'GeometryCollection'`, `'Circle'`.\n * @enum {string}\n */\nexport default {\n POINT: 'Point',\n LINE_STRING: 'LineString',\n LINEAR_RING: 'LinearRing',\n POLYGON: 'Polygon',\n MULTI_POINT: 'MultiPoint',\n MULTI_LINE_STRING: 'MultiLineString',\n MULTI_POLYGON: 'MultiPolygon',\n GEOMETRY_COLLECTION: 'GeometryCollection',\n CIRCLE: 'Circle'\n};\n//# sourceMappingURL=GeometryType.js.map","/**\n * @module ol/geom/flat/transform\n */\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {import(\"../../transform.js\").Transform} transform Transform.\n * @param {Array=} opt_dest Destination.\n * @return {Array} Transformed coordinates.\n */\nexport function transform2D(flatCoordinates, offset, end, stride, transform, opt_dest) {\n var dest = opt_dest ? opt_dest : [];\n var i = 0;\n for (var j = offset; j < end; j += stride) {\n var x = flatCoordinates[j];\n var y = flatCoordinates[j + 1];\n dest[i++] = transform[0] * x + transform[2] * y + transform[4];\n dest[i++] = transform[1] * x + transform[3] * y + transform[5];\n }\n if (opt_dest && dest.length != i) {\n dest.length = i;\n }\n return dest;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {number} angle Angle.\n * @param {Array} anchor Rotation anchor point.\n * @param {Array=} opt_dest Destination.\n * @return {Array} Transformed coordinates.\n */\nexport function rotate(flatCoordinates, offset, end, stride, angle, anchor, opt_dest) {\n var dest = opt_dest ? opt_dest : [];\n var cos = Math.cos(angle);\n var sin = Math.sin(angle);\n var anchorX = anchor[0];\n var anchorY = anchor[1];\n var i = 0;\n for (var j = offset; j < end; j += stride) {\n var deltaX = flatCoordinates[j] - anchorX;\n var deltaY = flatCoordinates[j + 1] - anchorY;\n dest[i++] = anchorX + deltaX * cos - deltaY * sin;\n dest[i++] = anchorY + deltaX * sin + deltaY * cos;\n for (var k = j + 2; k < j + stride; ++k) {\n dest[i++] = flatCoordinates[k];\n }\n }\n if (opt_dest && dest.length != i) {\n dest.length = i;\n }\n return dest;\n}\n/**\n * Scale the coordinates.\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {number} sx Scale factor in the x-direction.\n * @param {number} sy Scale factor in the y-direction.\n * @param {Array} anchor Scale anchor point.\n * @param {Array=} opt_dest Destination.\n * @return {Array} Transformed coordinates.\n */\nexport function scale(flatCoordinates, offset, end, stride, sx, sy, anchor, opt_dest) {\n var dest = opt_dest ? opt_dest : [];\n var anchorX = anchor[0];\n var anchorY = anchor[1];\n var i = 0;\n for (var j = offset; j < end; j += stride) {\n var deltaX = flatCoordinates[j] - anchorX;\n var deltaY = flatCoordinates[j + 1] - anchorY;\n dest[i++] = anchorX + sx * deltaX;\n dest[i++] = anchorY + sy * deltaY;\n for (var k = j + 2; k < j + stride; ++k) {\n dest[i++] = flatCoordinates[k];\n }\n }\n if (opt_dest && dest.length != i) {\n dest.length = i;\n }\n return dest;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {number} deltaX Delta X.\n * @param {number} deltaY Delta Y.\n * @param {Array=} opt_dest Destination.\n * @return {Array} Transformed coordinates.\n */\nexport function translate(flatCoordinates, offset, end, stride, deltaX, deltaY, opt_dest) {\n var dest = opt_dest ? opt_dest : [];\n var i = 0;\n for (var j = offset; j < end; j += stride) {\n dest[i++] = flatCoordinates[j] + deltaX;\n dest[i++] = flatCoordinates[j + 1] + deltaY;\n for (var k = j + 2; k < j + stride; ++k) {\n dest[i++] = flatCoordinates[k];\n }\n }\n if (opt_dest && dest.length != i) {\n dest.length = i;\n }\n return dest;\n}\n//# sourceMappingURL=transform.js.map","/**\n * @module ol/math\n */\n/**\n * Takes a number and clamps it to within the provided bounds.\n * @param {number} value The input number.\n * @param {number} min The minimum value to return.\n * @param {number} max The maximum value to return.\n * @return {number} The input number if it is within bounds, or the nearest\n * number within the bounds.\n */\nexport function clamp(value, min, max) {\n return Math.min(Math.max(value, min), max);\n}\n/**\n * Return the hyperbolic cosine of a given number. The method will use the\n * native `Math.cosh` function if it is available, otherwise the hyperbolic\n * cosine will be calculated via the reference implementation of the Mozilla\n * developer network.\n *\n * @param {number} x X.\n * @return {number} Hyperbolic cosine of x.\n */\nexport var cosh = (function () {\n // Wrapped in a iife, to save the overhead of checking for the native\n // implementation on every invocation.\n var cosh;\n if ('cosh' in Math) {\n // The environment supports the native Math.cosh function, use it…\n cosh = Math.cosh;\n }\n else {\n // … else, use the reference implementation of MDN:\n cosh = function (x) {\n var y = /** @type {Math} */ (Math).exp(x);\n return (y + 1 / y) / 2;\n };\n }\n return cosh;\n}());\n/**\n * Returns the square of the closest distance between the point (x, y) and the\n * line segment (x1, y1) to (x2, y2).\n * @param {number} x X.\n * @param {number} y Y.\n * @param {number} x1 X1.\n * @param {number} y1 Y1.\n * @param {number} x2 X2.\n * @param {number} y2 Y2.\n * @return {number} Squared distance.\n */\nexport function squaredSegmentDistance(x, y, x1, y1, x2, y2) {\n var dx = x2 - x1;\n var dy = y2 - y1;\n if (dx !== 0 || dy !== 0) {\n var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);\n if (t > 1) {\n x1 = x2;\n y1 = y2;\n }\n else if (t > 0) {\n x1 += dx * t;\n y1 += dy * t;\n }\n }\n return squaredDistance(x, y, x1, y1);\n}\n/**\n * Returns the square of the distance between the points (x1, y1) and (x2, y2).\n * @param {number} x1 X1.\n * @param {number} y1 Y1.\n * @param {number} x2 X2.\n * @param {number} y2 Y2.\n * @return {number} Squared distance.\n */\nexport function squaredDistance(x1, y1, x2, y2) {\n var dx = x2 - x1;\n var dy = y2 - y1;\n return dx * dx + dy * dy;\n}\n/**\n * Solves system of linear equations using Gaussian elimination method.\n *\n * @param {Array>} mat Augmented matrix (n x n + 1 column)\n * in row-major order.\n * @return {Array} The resulting vector.\n */\nexport function solveLinearSystem(mat) {\n var n = mat.length;\n for (var i = 0; i < n; i++) {\n // Find max in the i-th column (ignoring i - 1 first rows)\n var maxRow = i;\n var maxEl = Math.abs(mat[i][i]);\n for (var r = i + 1; r < n; r++) {\n var absValue = Math.abs(mat[r][i]);\n if (absValue > maxEl) {\n maxEl = absValue;\n maxRow = r;\n }\n }\n if (maxEl === 0) {\n return null; // matrix is singular\n }\n // Swap max row with i-th (current) row\n var tmp = mat[maxRow];\n mat[maxRow] = mat[i];\n mat[i] = tmp;\n // Subtract the i-th row to make all the remaining rows 0 in the i-th column\n for (var j = i + 1; j < n; j++) {\n var coef = -mat[j][i] / mat[i][i];\n for (var k = i; k < n + 1; k++) {\n if (i == k) {\n mat[j][k] = 0;\n }\n else {\n mat[j][k] += coef * mat[i][k];\n }\n }\n }\n }\n // Solve Ax=b for upper triangular matrix A (mat)\n var x = new Array(n);\n for (var l = n - 1; l >= 0; l--) {\n x[l] = mat[l][n] / mat[l][l];\n for (var m = l - 1; m >= 0; m--) {\n mat[m][n] -= mat[m][l] * x[l];\n }\n }\n return x;\n}\n/**\n * Converts radians to to degrees.\n *\n * @param {number} angleInRadians Angle in radians.\n * @return {number} Angle in degrees.\n */\nexport function toDegrees(angleInRadians) {\n return angleInRadians * 180 / Math.PI;\n}\n/**\n * Converts degrees to radians.\n *\n * @param {number} angleInDegrees Angle in degrees.\n * @return {number} Angle in radians.\n */\nexport function toRadians(angleInDegrees) {\n return angleInDegrees * Math.PI / 180;\n}\n/**\n * Returns the modulo of a / b, depending on the sign of b.\n *\n * @param {number} a Dividend.\n * @param {number} b Divisor.\n * @return {number} Modulo.\n */\nexport function modulo(a, b) {\n var r = a % b;\n return r * b < 0 ? r + b : r;\n}\n/**\n * Calculates the linearly interpolated value of x between a and b.\n *\n * @param {number} a Number\n * @param {number} b Number\n * @param {number} x Value to be interpolated.\n * @return {number} Interpolated value.\n */\nexport function lerp(a, b, x) {\n return a + x * (b - a);\n}\n//# sourceMappingURL=math.js.map","/**\n * @license\n * Latitude/longitude spherical geodesy formulae taken from\n * http://www.movable-type.co.uk/scripts/latlong.html\n * Licensed under CC-BY-3.0.\n */\n/**\n * @module ol/sphere\n */\nimport { toRadians, toDegrees } from './math.js';\nimport GeometryType from './geom/GeometryType.js';\n/**\n * Object literal with options for the {@link getLength} or {@link getArea}\n * functions.\n * @typedef {Object} SphereMetricOptions\n * @property {import(\"./proj.js\").ProjectionLike} [projection='EPSG:3857']\n * Projection of the geometry. By default, the geometry is assumed to be in\n * Web Mercator.\n * @property {number} [radius=6371008.8] Sphere radius. By default, the\n * [mean Earth radius](https://en.wikipedia.org/wiki/Earth_radius#Mean_radius)\n * for the WGS84 ellipsoid is used.\n */\n/**\n * The mean Earth radius (1/3 * (2a + b)) for the WGS84 ellipsoid.\n * https://en.wikipedia.org/wiki/Earth_radius#Mean_radius\n * @type {number}\n */\nexport var DEFAULT_RADIUS = 6371008.8;\n/**\n * Get the great circle distance (in meters) between two geographic coordinates.\n * @param {Array} c1 Starting coordinate.\n * @param {Array} c2 Ending coordinate.\n * @param {number=} opt_radius The sphere radius to use. Defaults to the Earth's\n * mean radius using the WGS84 ellipsoid.\n * @return {number} The great circle distance between the points (in meters).\n * @api\n */\nexport function getDistance(c1, c2, opt_radius) {\n var radius = opt_radius || DEFAULT_RADIUS;\n var lat1 = toRadians(c1[1]);\n var lat2 = toRadians(c2[1]);\n var deltaLatBy2 = (lat2 - lat1) / 2;\n var deltaLonBy2 = toRadians(c2[0] - c1[0]) / 2;\n var a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) +\n Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) *\n Math.cos(lat1) * Math.cos(lat2);\n return 2 * radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n}\n/**\n * Get the cumulative great circle length of linestring coordinates (geographic).\n * @param {Array} coordinates Linestring coordinates.\n * @param {number} radius The sphere radius to use.\n * @return {number} The length (in meters).\n */\nfunction getLengthInternal(coordinates, radius) {\n var length = 0;\n for (var i = 0, ii = coordinates.length; i < ii - 1; ++i) {\n length += getDistance(coordinates[i], coordinates[i + 1], radius);\n }\n return length;\n}\n/**\n * Get the spherical length of a geometry. This length is the sum of the\n * great circle distances between coordinates. For polygons, the length is\n * the sum of all rings. For points, the length is zero. For multi-part\n * geometries, the length is the sum of the length of each part.\n * @param {import(\"./geom/Geometry.js\").default} geometry A geometry.\n * @param {SphereMetricOptions=} opt_options Options for the\n * length calculation. By default, geometries are assumed to be in 'EPSG:3857'.\n * You can change this by providing a `projection` option.\n * @return {number} The spherical length (in meters).\n * @api\n */\nexport function getLength(geometry, opt_options) {\n var options = opt_options || {};\n var radius = options.radius || DEFAULT_RADIUS;\n var projection = options.projection || 'EPSG:3857';\n var type = geometry.getType();\n if (type !== GeometryType.GEOMETRY_COLLECTION) {\n geometry = geometry.clone().transform(projection, 'EPSG:4326');\n }\n var length = 0;\n var coordinates, coords, i, ii, j, jj;\n switch (type) {\n case GeometryType.POINT:\n case GeometryType.MULTI_POINT: {\n break;\n }\n case GeometryType.LINE_STRING:\n case GeometryType.LINEAR_RING: {\n coordinates = /** @type {import(\"./geom/SimpleGeometry.js\").default} */ (geometry).getCoordinates();\n length = getLengthInternal(coordinates, radius);\n break;\n }\n case GeometryType.MULTI_LINE_STRING:\n case GeometryType.POLYGON: {\n coordinates = /** @type {import(\"./geom/SimpleGeometry.js\").default} */ (geometry).getCoordinates();\n for (i = 0, ii = coordinates.length; i < ii; ++i) {\n length += getLengthInternal(coordinates[i], radius);\n }\n break;\n }\n case GeometryType.MULTI_POLYGON: {\n coordinates = /** @type {import(\"./geom/SimpleGeometry.js\").default} */ (geometry).getCoordinates();\n for (i = 0, ii = coordinates.length; i < ii; ++i) {\n coords = coordinates[i];\n for (j = 0, jj = coords.length; j < jj; ++j) {\n length += getLengthInternal(coords[j], radius);\n }\n }\n break;\n }\n case GeometryType.GEOMETRY_COLLECTION: {\n var geometries = /** @type {import(\"./geom/GeometryCollection.js\").default} */ (geometry).getGeometries();\n for (i = 0, ii = geometries.length; i < ii; ++i) {\n length += getLength(geometries[i], opt_options);\n }\n break;\n }\n default: {\n throw new Error('Unsupported geometry type: ' + type);\n }\n }\n return length;\n}\n/**\n * Returns the spherical area for a list of coordinates.\n *\n * [Reference](https://trs-new.jpl.nasa.gov/handle/2014/40409)\n * Robert. G. Chamberlain and William H. Duquette, \"Some Algorithms for\n * Polygons on a Sphere\", JPL Publication 07-03, Jet Propulsion\n * Laboratory, Pasadena, CA, June 2007\n *\n * @param {Array} coordinates List of coordinates of a linear\n * ring. If the ring is oriented clockwise, the area will be positive,\n * otherwise it will be negative.\n * @param {number} radius The sphere radius.\n * @return {number} Area (in square meters).\n */\nfunction getAreaInternal(coordinates, radius) {\n var area = 0;\n var len = coordinates.length;\n var x1 = coordinates[len - 1][0];\n var y1 = coordinates[len - 1][1];\n for (var i = 0; i < len; i++) {\n var x2 = coordinates[i][0];\n var y2 = coordinates[i][1];\n area += toRadians(x2 - x1) *\n (2 + Math.sin(toRadians(y1)) +\n Math.sin(toRadians(y2)));\n x1 = x2;\n y1 = y2;\n }\n return area * radius * radius / 2.0;\n}\n/**\n * Get the spherical area of a geometry. This is the area (in meters) assuming\n * that polygon edges are segments of great circles on a sphere.\n * @param {import(\"./geom/Geometry.js\").default} geometry A geometry.\n * @param {SphereMetricOptions=} opt_options Options for the area\n * calculation. By default, geometries are assumed to be in 'EPSG:3857'.\n * You can change this by providing a `projection` option.\n * @return {number} The spherical area (in square meters).\n * @api\n */\nexport function getArea(geometry, opt_options) {\n var options = opt_options || {};\n var radius = options.radius || DEFAULT_RADIUS;\n var projection = options.projection || 'EPSG:3857';\n var type = geometry.getType();\n if (type !== GeometryType.GEOMETRY_COLLECTION) {\n geometry = geometry.clone().transform(projection, 'EPSG:4326');\n }\n var area = 0;\n var coordinates, coords, i, ii, j, jj;\n switch (type) {\n case GeometryType.POINT:\n case GeometryType.MULTI_POINT:\n case GeometryType.LINE_STRING:\n case GeometryType.MULTI_LINE_STRING:\n case GeometryType.LINEAR_RING: {\n break;\n }\n case GeometryType.POLYGON: {\n coordinates = /** @type {import(\"./geom/Polygon.js\").default} */ (geometry).getCoordinates();\n area = Math.abs(getAreaInternal(coordinates[0], radius));\n for (i = 1, ii = coordinates.length; i < ii; ++i) {\n area -= Math.abs(getAreaInternal(coordinates[i], radius));\n }\n break;\n }\n case GeometryType.MULTI_POLYGON: {\n coordinates = /** @type {import(\"./geom/SimpleGeometry.js\").default} */ (geometry).getCoordinates();\n for (i = 0, ii = coordinates.length; i < ii; ++i) {\n coords = coordinates[i];\n area += Math.abs(getAreaInternal(coords[0], radius));\n for (j = 1, jj = coords.length; j < jj; ++j) {\n area -= Math.abs(getAreaInternal(coords[j], radius));\n }\n }\n break;\n }\n case GeometryType.GEOMETRY_COLLECTION: {\n var geometries = /** @type {import(\"./geom/GeometryCollection.js\").default} */ (geometry).getGeometries();\n for (i = 0, ii = geometries.length; i < ii; ++i) {\n area += getArea(geometries[i], opt_options);\n }\n break;\n }\n default: {\n throw new Error('Unsupported geometry type: ' + type);\n }\n }\n return area;\n}\n/**\n * Returns the coordinate at the given distance and bearing from `c1`.\n *\n * @param {import(\"./coordinate.js\").Coordinate} c1 The origin point (`[lon, lat]` in degrees).\n * @param {number} distance The great-circle distance between the origin\n * point and the target point.\n * @param {number} bearing The bearing (in radians).\n * @param {number=} opt_radius The sphere radius to use. Defaults to the Earth's\n * mean radius using the WGS84 ellipsoid.\n * @return {import(\"./coordinate.js\").Coordinate} The target point.\n */\nexport function offset(c1, distance, bearing, opt_radius) {\n var radius = opt_radius || DEFAULT_RADIUS;\n var lat1 = toRadians(c1[1]);\n var lon1 = toRadians(c1[0]);\n var dByR = distance / radius;\n var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) +\n Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));\n var lon = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));\n return [toDegrees(lon), toDegrees(lat)];\n}\n//# sourceMappingURL=sphere.js.map","/**\n * @module ol/proj/Units\n */\n/**\n * Projection units: `'degrees'`, `'ft'`, `'m'`, `'pixels'`, `'tile-pixels'` or\n * `'us-ft'`.\n * @enum {string}\n */\nvar Units = {\n DEGREES: 'degrees',\n FEET: 'ft',\n METERS: 'm',\n PIXELS: 'pixels',\n TILE_PIXELS: 'tile-pixels',\n USFEET: 'us-ft'\n};\n/**\n * Meters per unit lookup table.\n * @const\n * @type {Object}\n * @api\n */\nexport var METERS_PER_UNIT = {};\n// use the radius of the Normal sphere\nMETERS_PER_UNIT[Units.DEGREES] = 2 * Math.PI * 6370997 / 360;\nMETERS_PER_UNIT[Units.FEET] = 0.3048;\nMETERS_PER_UNIT[Units.METERS] = 1;\nMETERS_PER_UNIT[Units.USFEET] = 1200 / 3937;\nexport default Units;\n//# sourceMappingURL=Units.js.map","/**\n * @module ol/proj/Projection\n */\nimport { METERS_PER_UNIT } from './Units.js';\n/**\n * @typedef {Object} Options\n * @property {string} code The SRS identifier code, e.g. `EPSG:4326`.\n * @property {import(\"./Units.js\").default|string} [units] Units. Required unless a\n * proj4 projection is defined for `code`.\n * @property {import(\"../extent.js\").Extent} [extent] The validity extent for the SRS.\n * @property {string} [axisOrientation='enu'] The axis orientation as specified in Proj4.\n * @property {boolean} [global=false] Whether the projection is valid for the whole globe.\n * @property {number} [metersPerUnit] The meters per unit for the SRS.\n * If not provided, the `units` are used to get the meters per unit from the {@link module:ol/proj/Units~METERS_PER_UNIT}\n * lookup table.\n * @property {import(\"../extent.js\").Extent} [worldExtent] The world extent for the SRS.\n * @property {function(number, import(\"../coordinate.js\").Coordinate):number} [getPointResolution]\n * Function to determine resolution at a point. The function is called with a\n * `{number}` view resolution and an `{import(\"../coordinate.js\").Coordinate}` as arguments, and returns\n * the `{number}` resolution in projection units at the passed coordinate. If this is `undefined`,\n * the default {@link module:ol/proj#getPointResolution} function will be used.\n */\n/**\n * @classdesc\n * Projection definition class. One of these is created for each projection\n * supported in the application and stored in the {@link module:ol/proj} namespace.\n * You can use these in applications, but this is not required, as API params\n * and options use {@link module:ol/proj~ProjectionLike} which means the simple string\n * code will suffice.\n *\n * You can use {@link module:ol/proj~get} to retrieve the object for a particular\n * projection.\n *\n * The library includes definitions for `EPSG:4326` and `EPSG:3857`, together\n * with the following aliases:\n * * `EPSG:4326`: CRS:84, urn:ogc:def:crs:EPSG:6.6:4326,\n * urn:ogc:def:crs:OGC:1.3:CRS84, urn:ogc:def:crs:OGC:2:84,\n * http://www.opengis.net/gml/srs/epsg.xml#4326,\n * urn:x-ogc:def:crs:EPSG:4326\n * * `EPSG:3857`: EPSG:102100, EPSG:102113, EPSG:900913,\n * urn:ogc:def:crs:EPSG:6.18:3:3857,\n * http://www.opengis.net/gml/srs/epsg.xml#3857\n *\n * If you use [proj4js](https://github.com/proj4js/proj4js), aliases can\n * be added using `proj4.defs()`. After all required projection definitions are\n * added, call the {@link module:ol/proj/proj4~register} function.\n *\n * @api\n */\nvar Projection = /** @class */ (function () {\n /**\n * @param {Options} options Projection options.\n */\n function Projection(options) {\n /**\n * @private\n * @type {string}\n */\n this.code_ = options.code;\n /**\n * Units of projected coordinates. When set to `TILE_PIXELS`, a\n * `this.extent_` and `this.worldExtent_` must be configured properly for each\n * tile.\n * @private\n * @type {import(\"./Units.js\").default}\n */\n this.units_ = /** @type {import(\"./Units.js\").default} */ (options.units);\n /**\n * Validity extent of the projection in projected coordinates. For projections\n * with `TILE_PIXELS` units, this is the extent of the tile in\n * tile pixel space.\n * @private\n * @type {import(\"../extent.js\").Extent}\n */\n this.extent_ = options.extent !== undefined ? options.extent : null;\n /**\n * Extent of the world in EPSG:4326. For projections with\n * `TILE_PIXELS` units, this is the extent of the tile in\n * projected coordinate space.\n * @private\n * @type {import(\"../extent.js\").Extent}\n */\n this.worldExtent_ = options.worldExtent !== undefined ?\n options.worldExtent : null;\n /**\n * @private\n * @type {string}\n */\n this.axisOrientation_ = options.axisOrientation !== undefined ?\n options.axisOrientation : 'enu';\n /**\n * @private\n * @type {boolean}\n */\n this.global_ = options.global !== undefined ? options.global : false;\n /**\n * @private\n * @type {boolean}\n */\n this.canWrapX_ = !!(this.global_ && this.extent_);\n /**\n * @private\n * @type {function(number, import(\"../coordinate.js\").Coordinate):number|undefined}\n */\n this.getPointResolutionFunc_ = options.getPointResolution;\n /**\n * @private\n * @type {import(\"../tilegrid/TileGrid.js\").default}\n */\n this.defaultTileGrid_ = null;\n /**\n * @private\n * @type {number|undefined}\n */\n this.metersPerUnit_ = options.metersPerUnit;\n }\n /**\n * @return {boolean} The projection is suitable for wrapping the x-axis\n */\n Projection.prototype.canWrapX = function () {\n return this.canWrapX_;\n };\n /**\n * Get the code for this projection, e.g. 'EPSG:4326'.\n * @return {string} Code.\n * @api\n */\n Projection.prototype.getCode = function () {\n return this.code_;\n };\n /**\n * Get the validity extent for this projection.\n * @return {import(\"../extent.js\").Extent} Extent.\n * @api\n */\n Projection.prototype.getExtent = function () {\n return this.extent_;\n };\n /**\n * Get the units of this projection.\n * @return {import(\"./Units.js\").default} Units.\n * @api\n */\n Projection.prototype.getUnits = function () {\n return this.units_;\n };\n /**\n * Get the amount of meters per unit of this projection. If the projection is\n * not configured with `metersPerUnit` or a units identifier, the return is\n * `undefined`.\n * @return {number|undefined} Meters.\n * @api\n */\n Projection.prototype.getMetersPerUnit = function () {\n return this.metersPerUnit_ || METERS_PER_UNIT[this.units_];\n };\n /**\n * Get the world extent for this projection.\n * @return {import(\"../extent.js\").Extent} Extent.\n * @api\n */\n Projection.prototype.getWorldExtent = function () {\n return this.worldExtent_;\n };\n /**\n * Get the axis orientation of this projection.\n * Example values are:\n * enu - the default easting, northing, elevation.\n * neu - northing, easting, up - useful for \"lat/long\" geographic coordinates,\n * or south orientated transverse mercator.\n * wnu - westing, northing, up - some planetary coordinate systems have\n * \"west positive\" coordinate systems\n * @return {string} Axis orientation.\n * @api\n */\n Projection.prototype.getAxisOrientation = function () {\n return this.axisOrientation_;\n };\n /**\n * Is this projection a global projection which spans the whole world?\n * @return {boolean} Whether the projection is global.\n * @api\n */\n Projection.prototype.isGlobal = function () {\n return this.global_;\n };\n /**\n * Set if the projection is a global projection which spans the whole world\n * @param {boolean} global Whether the projection is global.\n * @api\n */\n Projection.prototype.setGlobal = function (global) {\n this.global_ = global;\n this.canWrapX_ = !!(global && this.extent_);\n };\n /**\n * @return {import(\"../tilegrid/TileGrid.js\").default} The default tile grid.\n */\n Projection.prototype.getDefaultTileGrid = function () {\n return this.defaultTileGrid_;\n };\n /**\n * @param {import(\"../tilegrid/TileGrid.js\").default} tileGrid The default tile grid.\n */\n Projection.prototype.setDefaultTileGrid = function (tileGrid) {\n this.defaultTileGrid_ = tileGrid;\n };\n /**\n * Set the validity extent for this projection.\n * @param {import(\"../extent.js\").Extent} extent Extent.\n * @api\n */\n Projection.prototype.setExtent = function (extent) {\n this.extent_ = extent;\n this.canWrapX_ = !!(this.global_ && extent);\n };\n /**\n * Set the world extent for this projection.\n * @param {import(\"../extent.js\").Extent} worldExtent World extent\n * [minlon, minlat, maxlon, maxlat].\n * @api\n */\n Projection.prototype.setWorldExtent = function (worldExtent) {\n this.worldExtent_ = worldExtent;\n };\n /**\n * Set the getPointResolution function (see {@link module:ol/proj~getPointResolution}\n * for this projection.\n * @param {function(number, import(\"../coordinate.js\").Coordinate):number} func Function\n * @api\n */\n Projection.prototype.setGetPointResolution = function (func) {\n this.getPointResolutionFunc_ = func;\n };\n /**\n * Get the custom point resolution function for this projection (if set).\n * @return {function(number, import(\"../coordinate.js\").Coordinate):number|undefined} The custom point\n * resolution function (if set).\n */\n Projection.prototype.getPointResolutionFunc = function () {\n return this.getPointResolutionFunc_;\n };\n return Projection;\n}());\nexport default Projection;\n//# sourceMappingURL=Projection.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/proj/epsg3857\n */\nimport { cosh } from '../math.js';\nimport Projection from './Projection.js';\nimport Units from './Units.js';\n/**\n * Radius of WGS84 sphere\n *\n * @const\n * @type {number}\n */\nexport var RADIUS = 6378137;\n/**\n * @const\n * @type {number}\n */\nexport var HALF_SIZE = Math.PI * RADIUS;\n/**\n * @const\n * @type {import(\"../extent.js\").Extent}\n */\nexport var EXTENT = [\n -HALF_SIZE, -HALF_SIZE,\n HALF_SIZE, HALF_SIZE\n];\n/**\n * @const\n * @type {import(\"../extent.js\").Extent}\n */\nexport var WORLD_EXTENT = [-180, -85, 180, 85];\n/**\n * @classdesc\n * Projection object for web/spherical Mercator (EPSG:3857).\n */\nvar EPSG3857Projection = /** @class */ (function (_super) {\n __extends(EPSG3857Projection, _super);\n /**\n * @param {string} code Code.\n */\n function EPSG3857Projection(code) {\n return _super.call(this, {\n code: code,\n units: Units.METERS,\n extent: EXTENT,\n global: true,\n worldExtent: WORLD_EXTENT,\n getPointResolution: function (resolution, point) {\n return resolution / cosh(point[1] / RADIUS);\n }\n }) || this;\n }\n return EPSG3857Projection;\n}(Projection));\n/**\n * Projections equal to EPSG:3857.\n *\n * @const\n * @type {Array}\n */\nexport var PROJECTIONS = [\n new EPSG3857Projection('EPSG:3857'),\n new EPSG3857Projection('EPSG:102100'),\n new EPSG3857Projection('EPSG:102113'),\n new EPSG3857Projection('EPSG:900913'),\n new EPSG3857Projection('urn:ogc:def:crs:EPSG:6.18:3:3857'),\n new EPSG3857Projection('urn:ogc:def:crs:EPSG::3857'),\n new EPSG3857Projection('http://www.opengis.net/gml/srs/epsg.xml#3857')\n];\n/**\n * Transformation from EPSG:4326 to EPSG:3857.\n *\n * @param {Array} input Input array of coordinate values.\n * @param {Array=} opt_output Output array of coordinate values.\n * @param {number=} opt_dimension Dimension (default is `2`).\n * @return {Array} Output array of coordinate values.\n */\nexport function fromEPSG4326(input, opt_output, opt_dimension) {\n var length = input.length;\n var dimension = opt_dimension > 1 ? opt_dimension : 2;\n var output = opt_output;\n if (output === undefined) {\n if (dimension > 2) {\n // preserve values beyond second dimension\n output = input.slice();\n }\n else {\n output = new Array(length);\n }\n }\n var halfSize = HALF_SIZE;\n for (var i = 0; i < length; i += dimension) {\n output[i] = halfSize * input[i] / 180;\n var y = RADIUS *\n Math.log(Math.tan(Math.PI * (+input[i + 1] + 90) / 360));\n if (y > halfSize) {\n y = halfSize;\n }\n else if (y < -halfSize) {\n y = -halfSize;\n }\n output[i + 1] = y;\n }\n return output;\n}\n/**\n * Transformation from EPSG:3857 to EPSG:4326.\n *\n * @param {Array} input Input array of coordinate values.\n * @param {Array=} opt_output Output array of coordinate values.\n * @param {number=} opt_dimension Dimension (default is `2`).\n * @return {Array} Output array of coordinate values.\n */\nexport function toEPSG4326(input, opt_output, opt_dimension) {\n var length = input.length;\n var dimension = opt_dimension > 1 ? opt_dimension : 2;\n var output = opt_output;\n if (output === undefined) {\n if (dimension > 2) {\n // preserve values beyond second dimension\n output = input.slice();\n }\n else {\n output = new Array(length);\n }\n }\n for (var i = 0; i < length; i += dimension) {\n output[i] = 180 * input[i] / HALF_SIZE;\n output[i + 1] = 360 * Math.atan(Math.exp(input[i + 1] / RADIUS)) / Math.PI - 90;\n }\n return output;\n}\n//# sourceMappingURL=epsg3857.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/proj/epsg4326\n */\nimport Projection from './Projection.js';\nimport Units from './Units.js';\n/**\n * Semi-major radius of the WGS84 ellipsoid.\n *\n * @const\n * @type {number}\n */\nexport var RADIUS = 6378137;\n/**\n * Extent of the EPSG:4326 projection which is the whole world.\n *\n * @const\n * @type {import(\"../extent.js\").Extent}\n */\nexport var EXTENT = [-180, -90, 180, 90];\n/**\n * @const\n * @type {number}\n */\nexport var METERS_PER_UNIT = Math.PI * RADIUS / 180;\n/**\n * @classdesc\n * Projection object for WGS84 geographic coordinates (EPSG:4326).\n *\n * Note that OpenLayers does not strictly comply with the EPSG definition.\n * The EPSG registry defines 4326 as a CRS for Latitude,Longitude (y,x).\n * OpenLayers treats EPSG:4326 as a pseudo-projection, with x,y coordinates.\n */\nvar EPSG4326Projection = /** @class */ (function (_super) {\n __extends(EPSG4326Projection, _super);\n /**\n * @param {string} code Code.\n * @param {string=} opt_axisOrientation Axis orientation.\n */\n function EPSG4326Projection(code, opt_axisOrientation) {\n return _super.call(this, {\n code: code,\n units: Units.DEGREES,\n extent: EXTENT,\n axisOrientation: opt_axisOrientation,\n global: true,\n metersPerUnit: METERS_PER_UNIT,\n worldExtent: EXTENT\n }) || this;\n }\n return EPSG4326Projection;\n}(Projection));\n/**\n * Projections equal to EPSG:4326.\n *\n * @const\n * @type {Array}\n */\nexport var PROJECTIONS = [\n new EPSG4326Projection('CRS:84'),\n new EPSG4326Projection('EPSG:4326', 'neu'),\n new EPSG4326Projection('urn:ogc:def:crs:EPSG::4326', 'neu'),\n new EPSG4326Projection('urn:ogc:def:crs:EPSG:6.6:4326', 'neu'),\n new EPSG4326Projection('urn:ogc:def:crs:OGC:1.3:CRS84'),\n new EPSG4326Projection('urn:ogc:def:crs:OGC:2:84'),\n new EPSG4326Projection('http://www.opengis.net/gml/srs/epsg.xml#4326', 'neu'),\n new EPSG4326Projection('urn:x-ogc:def:crs:EPSG:4326', 'neu')\n];\n//# sourceMappingURL=epsg4326.js.map","/**\n * @module ol/proj/transforms\n */\nimport { isEmpty } from '../obj.js';\n/**\n * @private\n * @type {!Object>}\n */\nvar transforms = {};\n/**\n * Clear the transform cache.\n */\nexport function clear() {\n transforms = {};\n}\n/**\n * Registers a conversion function to convert coordinates from the source\n * projection to the destination projection.\n *\n * @param {import(\"./Projection.js\").default} source Source.\n * @param {import(\"./Projection.js\").default} destination Destination.\n * @param {import(\"../proj.js\").TransformFunction} transformFn Transform.\n */\nexport function add(source, destination, transformFn) {\n var sourceCode = source.getCode();\n var destinationCode = destination.getCode();\n if (!(sourceCode in transforms)) {\n transforms[sourceCode] = {};\n }\n transforms[sourceCode][destinationCode] = transformFn;\n}\n/**\n * Unregisters the conversion function to convert coordinates from the source\n * projection to the destination projection. This method is used to clean up\n * cached transforms during testing.\n *\n * @param {import(\"./Projection.js\").default} source Source projection.\n * @param {import(\"./Projection.js\").default} destination Destination projection.\n * @return {import(\"../proj.js\").TransformFunction} transformFn The unregistered transform.\n */\nexport function remove(source, destination) {\n var sourceCode = source.getCode();\n var destinationCode = destination.getCode();\n var transform = transforms[sourceCode][destinationCode];\n delete transforms[sourceCode][destinationCode];\n if (isEmpty(transforms[sourceCode])) {\n delete transforms[sourceCode];\n }\n return transform;\n}\n/**\n * Get a transform given a source code and a destination code.\n * @param {string} sourceCode The code for the source projection.\n * @param {string} destinationCode The code for the destination projection.\n * @return {import(\"../proj.js\").TransformFunction|undefined} The transform function (if found).\n */\nexport function get(sourceCode, destinationCode) {\n var transform;\n if (sourceCode in transforms && destinationCode in transforms[sourceCode]) {\n transform = transforms[sourceCode][destinationCode];\n }\n return transform;\n}\n//# sourceMappingURL=transforms.js.map","/**\n * @module ol/proj/projections\n */\n/**\n * @type {Object}\n */\nvar cache = {};\n/**\n * Clear the projections cache.\n */\nexport function clear() {\n cache = {};\n}\n/**\n * Get a cached projection by code.\n * @param {string} code The code for the projection.\n * @return {import(\"./Projection.js\").default} The projection (if cached).\n */\nexport function get(code) {\n return cache[code] || null;\n}\n/**\n * Add a projection to the cache.\n * @param {string} code The projection code.\n * @param {import(\"./Projection.js\").default} projection The projection to cache.\n */\nexport function add(code, projection) {\n cache[code] = projection;\n}\n//# sourceMappingURL=projections.js.map","/**\n * @module ol/proj\n */\n/**\n * The ol/proj module stores:\n * * a list of {@link module:ol/proj/Projection}\n * objects, one for each projection supported by the application\n * * a list of transform functions needed to convert coordinates in one projection\n * into another.\n *\n * The static functions are the methods used to maintain these.\n * Each transform function can handle not only simple coordinate pairs, but also\n * large arrays of coordinates such as vector geometries.\n *\n * When loaded, the library adds projection objects for EPSG:4326 (WGS84\n * geographic coordinates) and EPSG:3857 (Web or Spherical Mercator, as used\n * for example by Bing Maps or OpenStreetMap), together with the relevant\n * transform functions.\n *\n * Additional transforms may be added by using the http://proj4js.org/\n * library (version 2.2 or later). You can use the full build supplied by\n * Proj4js, or create a custom build to support those projections you need; see\n * the Proj4js website for how to do this. You also need the Proj4js definitions\n * for the required projections. These definitions can be obtained from\n * https://epsg.io/, and are a JS function, so can be loaded in a script\n * tag (as in the examples) or pasted into your application.\n *\n * After all required projection definitions are added to proj4's registry (by\n * using `proj4.defs()`), simply call `register(proj4)` from the `ol/proj/proj4`\n * package. Existing transforms are not changed by this function. See\n * examples/wms-image-custom-proj for an example of this.\n *\n * Additional projection definitions can be registered with `proj4.defs()` any\n * time. Just make sure to call `register(proj4)` again; for example, with user-supplied data where you don't\n * know in advance what projections are needed, you can initially load minimal\n * support and then load whichever are requested.\n *\n * Note that Proj4js does not support projection extents. If you want to add\n * one for creating default tile grids, you can add it after the Projection\n * object has been created with `setExtent`, for example,\n * `get('EPSG:1234').setExtent(extent)`.\n *\n * In addition to Proj4js support, any transform functions can be added with\n * {@link module:ol/proj~addCoordinateTransforms}. To use this, you must first create\n * a {@link module:ol/proj/Projection} object for the new projection and add it with\n * {@link module:ol/proj~addProjection}. You can then add the forward and inverse\n * functions with {@link module:ol/proj~addCoordinateTransforms}. See\n * examples/wms-custom-proj for an example of this.\n *\n * Note that if no transforms are needed and you only need to define the\n * projection, just add a {@link module:ol/proj/Projection} with\n * {@link module:ol/proj~addProjection}. See examples/wms-no-proj for an example of\n * this.\n */\nimport { getDistance } from './sphere.js';\nimport { applyTransform } from './extent.js';\nimport { modulo } from './math.js';\nimport { toEPSG4326, fromEPSG4326, PROJECTIONS as EPSG3857_PROJECTIONS } from './proj/epsg3857.js';\nimport { PROJECTIONS as EPSG4326_PROJECTIONS } from './proj/epsg4326.js';\nimport Projection from './proj/Projection.js';\nimport Units, { METERS_PER_UNIT } from './proj/Units.js';\nimport { add as addTransformFunc, clear as clearTransformFuncs, get as getTransformFunc } from './proj/transforms.js';\nimport { add as addProj, clear as clearProj, get as getProj } from './proj/projections.js';\n/**\n * A projection as {@link module:ol/proj/Projection}, SRS identifier\n * string or undefined.\n * @typedef {Projection|string|undefined} ProjectionLike\n * @api\n */\n/**\n * A transform function accepts an array of input coordinate values, an optional\n * output array, and an optional dimension (default should be 2). The function\n * transforms the input coordinate values, populates the output array, and\n * returns the output array.\n *\n * @typedef {function(Array, Array=, number=): Array} TransformFunction\n * @api\n */\nexport { METERS_PER_UNIT };\nexport { Projection };\n/**\n * @param {Array} input Input coordinate array.\n * @param {Array=} opt_output Output array of coordinate values.\n * @param {number=} opt_dimension Dimension.\n * @return {Array} Output coordinate array (new array, same coordinate\n * values).\n */\nexport function cloneTransform(input, opt_output, opt_dimension) {\n var output;\n if (opt_output !== undefined) {\n for (var i = 0, ii = input.length; i < ii; ++i) {\n opt_output[i] = input[i];\n }\n output = opt_output;\n }\n else {\n output = input.slice();\n }\n return output;\n}\n/**\n * @param {Array} input Input coordinate array.\n * @param {Array=} opt_output Output array of coordinate values.\n * @param {number=} opt_dimension Dimension.\n * @return {Array} Input coordinate array (same array as input).\n */\nexport function identityTransform(input, opt_output, opt_dimension) {\n if (opt_output !== undefined && input !== opt_output) {\n for (var i = 0, ii = input.length; i < ii; ++i) {\n opt_output[i] = input[i];\n }\n input = opt_output;\n }\n return input;\n}\n/**\n * Add a Projection object to the list of supported projections that can be\n * looked up by their code.\n *\n * @param {Projection} projection Projection instance.\n * @api\n */\nexport function addProjection(projection) {\n addProj(projection.getCode(), projection);\n addTransformFunc(projection, projection, cloneTransform);\n}\n/**\n * @param {Array} projections Projections.\n */\nexport function addProjections(projections) {\n projections.forEach(addProjection);\n}\n/**\n * Fetches a Projection object for the code specified.\n *\n * @param {ProjectionLike} projectionLike Either a code string which is\n * a combination of authority and identifier such as \"EPSG:4326\", or an\n * existing projection object, or undefined.\n * @return {Projection} Projection object, or null if not in list.\n * @api\n */\nexport function get(projectionLike) {\n return typeof projectionLike === 'string' ?\n getProj(/** @type {string} */ (projectionLike)) :\n ( /** @type {Projection} */(projectionLike) || null);\n}\n/**\n * Get the resolution of the point in degrees or distance units.\n * For projections with degrees as the unit this will simply return the\n * provided resolution. For other projections the point resolution is\n * by default estimated by transforming the 'point' pixel to EPSG:4326,\n * measuring its width and height on the normal sphere,\n * and taking the average of the width and height.\n * A custom function can be provided for a specific projection, either\n * by setting the `getPointResolution` option in the\n * {@link module:ol/proj/Projection~Projection} constructor or by using\n * {@link module:ol/proj/Projection~Projection#setGetPointResolution} to change an existing\n * projection object.\n * @param {ProjectionLike} projection The projection.\n * @param {number} resolution Nominal resolution in projection units.\n * @param {import(\"./coordinate.js\").Coordinate} point Point to find adjusted resolution at.\n * @param {Units=} opt_units Units to get the point resolution in.\n * Default is the projection's units.\n * @return {number} Point resolution.\n * @api\n */\nexport function getPointResolution(projection, resolution, point, opt_units) {\n projection = get(projection);\n var pointResolution;\n var getter = projection.getPointResolutionFunc();\n if (getter) {\n pointResolution = getter(resolution, point);\n if (opt_units && opt_units !== projection.getUnits()) {\n var metersPerUnit = projection.getMetersPerUnit();\n if (metersPerUnit) {\n pointResolution = pointResolution * metersPerUnit / METERS_PER_UNIT[opt_units];\n }\n }\n }\n else {\n var units = projection.getUnits();\n if (units == Units.DEGREES && !opt_units || opt_units == Units.DEGREES) {\n pointResolution = resolution;\n }\n else {\n // Estimate point resolution by transforming the center pixel to EPSG:4326,\n // measuring its width and height on the normal sphere, and taking the\n // average of the width and height.\n var toEPSG4326_1 = getTransformFromProjections(projection, get('EPSG:4326'));\n var vertices = [\n point[0] - resolution / 2, point[1],\n point[0] + resolution / 2, point[1],\n point[0], point[1] - resolution / 2,\n point[0], point[1] + resolution / 2\n ];\n vertices = toEPSG4326_1(vertices, vertices, 2);\n var width = getDistance(vertices.slice(0, 2), vertices.slice(2, 4));\n var height = getDistance(vertices.slice(4, 6), vertices.slice(6, 8));\n pointResolution = (width + height) / 2;\n var metersPerUnit = opt_units ?\n METERS_PER_UNIT[opt_units] :\n projection.getMetersPerUnit();\n if (metersPerUnit !== undefined) {\n pointResolution /= metersPerUnit;\n }\n }\n }\n return pointResolution;\n}\n/**\n * Registers transformation functions that don't alter coordinates. Those allow\n * to transform between projections with equal meaning.\n *\n * @param {Array} projections Projections.\n * @api\n */\nexport function addEquivalentProjections(projections) {\n addProjections(projections);\n projections.forEach(function (source) {\n projections.forEach(function (destination) {\n if (source !== destination) {\n addTransformFunc(source, destination, cloneTransform);\n }\n });\n });\n}\n/**\n * Registers transformation functions to convert coordinates in any projection\n * in projection1 to any projection in projection2.\n *\n * @param {Array} projections1 Projections with equal\n * meaning.\n * @param {Array} projections2 Projections with equal\n * meaning.\n * @param {TransformFunction} forwardTransform Transformation from any\n * projection in projection1 to any projection in projection2.\n * @param {TransformFunction} inverseTransform Transform from any projection\n * in projection2 to any projection in projection1..\n */\nexport function addEquivalentTransforms(projections1, projections2, forwardTransform, inverseTransform) {\n projections1.forEach(function (projection1) {\n projections2.forEach(function (projection2) {\n addTransformFunc(projection1, projection2, forwardTransform);\n addTransformFunc(projection2, projection1, inverseTransform);\n });\n });\n}\n/**\n * Clear all cached projections and transforms.\n */\nexport function clearAllProjections() {\n clearProj();\n clearTransformFuncs();\n}\n/**\n * @param {Projection|string|undefined} projection Projection.\n * @param {string} defaultCode Default code.\n * @return {Projection} Projection.\n */\nexport function createProjection(projection, defaultCode) {\n if (!projection) {\n return get(defaultCode);\n }\n else if (typeof projection === 'string') {\n return get(projection);\n }\n else {\n return (\n /** @type {Projection} */ (projection));\n }\n}\n/**\n * Creates a {@link module:ol/proj~TransformFunction} from a simple 2D coordinate transform\n * function.\n * @param {function(import(\"./coordinate.js\").Coordinate): import(\"./coordinate.js\").Coordinate} coordTransform Coordinate\n * transform.\n * @return {TransformFunction} Transform function.\n */\nexport function createTransformFromCoordinateTransform(coordTransform) {\n return (\n /**\n * @param {Array} input Input.\n * @param {Array=} opt_output Output.\n * @param {number=} opt_dimension Dimension.\n * @return {Array} Output.\n */\n function (input, opt_output, opt_dimension) {\n var length = input.length;\n var dimension = opt_dimension !== undefined ? opt_dimension : 2;\n var output = opt_output !== undefined ? opt_output : new Array(length);\n for (var i = 0; i < length; i += dimension) {\n var point = coordTransform([input[i], input[i + 1]]);\n output[i] = point[0];\n output[i + 1] = point[1];\n for (var j = dimension - 1; j >= 2; --j) {\n output[i + j] = input[i + j];\n }\n }\n return output;\n });\n}\n/**\n * Registers coordinate transform functions to convert coordinates between the\n * source projection and the destination projection.\n * The forward and inverse functions convert coordinate pairs; this function\n * converts these into the functions used internally which also handle\n * extents and coordinate arrays.\n *\n * @param {ProjectionLike} source Source projection.\n * @param {ProjectionLike} destination Destination projection.\n * @param {function(import(\"./coordinate.js\").Coordinate): import(\"./coordinate.js\").Coordinate} forward The forward transform\n * function (that is, from the source projection to the destination\n * projection) that takes a {@link module:ol/coordinate~Coordinate} as argument and returns\n * the transformed {@link module:ol/coordinate~Coordinate}.\n * @param {function(import(\"./coordinate.js\").Coordinate): import(\"./coordinate.js\").Coordinate} inverse The inverse transform\n * function (that is, from the destination projection to the source\n * projection) that takes a {@link module:ol/coordinate~Coordinate} as argument and returns\n * the transformed {@link module:ol/coordinate~Coordinate}.\n * @api\n */\nexport function addCoordinateTransforms(source, destination, forward, inverse) {\n var sourceProj = get(source);\n var destProj = get(destination);\n addTransformFunc(sourceProj, destProj, createTransformFromCoordinateTransform(forward));\n addTransformFunc(destProj, sourceProj, createTransformFromCoordinateTransform(inverse));\n}\n/**\n * Transforms a coordinate from longitude/latitude to a different projection.\n * @param {import(\"./coordinate.js\").Coordinate} coordinate Coordinate as longitude and latitude, i.e.\n * an array with longitude as 1st and latitude as 2nd element.\n * @param {ProjectionLike=} opt_projection Target projection. The\n * default is Web Mercator, i.e. 'EPSG:3857'.\n * @return {import(\"./coordinate.js\").Coordinate} Coordinate projected to the target projection.\n * @api\n */\nexport function fromLonLat(coordinate, opt_projection) {\n return transform(coordinate, 'EPSG:4326', opt_projection !== undefined ? opt_projection : 'EPSG:3857');\n}\n/**\n * Transforms a coordinate to longitude/latitude.\n * @param {import(\"./coordinate.js\").Coordinate} coordinate Projected coordinate.\n * @param {ProjectionLike=} opt_projection Projection of the coordinate.\n * The default is Web Mercator, i.e. 'EPSG:3857'.\n * @return {import(\"./coordinate.js\").Coordinate} Coordinate as longitude and latitude, i.e. an array\n * with longitude as 1st and latitude as 2nd element.\n * @api\n */\nexport function toLonLat(coordinate, opt_projection) {\n var lonLat = transform(coordinate, opt_projection !== undefined ? opt_projection : 'EPSG:3857', 'EPSG:4326');\n var lon = lonLat[0];\n if (lon < -180 || lon > 180) {\n lonLat[0] = modulo(lon + 180, 360) - 180;\n }\n return lonLat;\n}\n/**\n * Checks if two projections are the same, that is every coordinate in one\n * projection does represent the same geographic point as the same coordinate in\n * the other projection.\n *\n * @param {Projection} projection1 Projection 1.\n * @param {Projection} projection2 Projection 2.\n * @return {boolean} Equivalent.\n * @api\n */\nexport function equivalent(projection1, projection2) {\n if (projection1 === projection2) {\n return true;\n }\n var equalUnits = projection1.getUnits() === projection2.getUnits();\n if (projection1.getCode() === projection2.getCode()) {\n return equalUnits;\n }\n else {\n var transformFunc = getTransformFromProjections(projection1, projection2);\n return transformFunc === cloneTransform && equalUnits;\n }\n}\n/**\n * Searches in the list of transform functions for the function for converting\n * coordinates from the source projection to the destination projection.\n *\n * @param {Projection} sourceProjection Source Projection object.\n * @param {Projection} destinationProjection Destination Projection\n * object.\n * @return {TransformFunction} Transform function.\n */\nexport function getTransformFromProjections(sourceProjection, destinationProjection) {\n var sourceCode = sourceProjection.getCode();\n var destinationCode = destinationProjection.getCode();\n var transformFunc = getTransformFunc(sourceCode, destinationCode);\n if (!transformFunc) {\n transformFunc = identityTransform;\n }\n return transformFunc;\n}\n/**\n * Given the projection-like objects, searches for a transformation\n * function to convert a coordinates array from the source projection to the\n * destination projection.\n *\n * @param {ProjectionLike} source Source.\n * @param {ProjectionLike} destination Destination.\n * @return {TransformFunction} Transform function.\n * @api\n */\nexport function getTransform(source, destination) {\n var sourceProjection = get(source);\n var destinationProjection = get(destination);\n return getTransformFromProjections(sourceProjection, destinationProjection);\n}\n/**\n * Transforms a coordinate from source projection to destination projection.\n * This returns a new coordinate (and does not modify the original).\n *\n * See {@link module:ol/proj~transformExtent} for extent transformation.\n * See the transform method of {@link module:ol/geom/Geometry~Geometry} and its\n * subclasses for geometry transforms.\n *\n * @param {import(\"./coordinate.js\").Coordinate} coordinate Coordinate.\n * @param {ProjectionLike} source Source projection-like.\n * @param {ProjectionLike} destination Destination projection-like.\n * @return {import(\"./coordinate.js\").Coordinate} Coordinate.\n * @api\n */\nexport function transform(coordinate, source, destination) {\n var transformFunc = getTransform(source, destination);\n return transformFunc(coordinate, undefined, coordinate.length);\n}\n/**\n * Transforms an extent from source projection to destination projection. This\n * returns a new extent (and does not modify the original).\n *\n * @param {import(\"./extent.js\").Extent} extent The extent to transform.\n * @param {ProjectionLike} source Source projection-like.\n * @param {ProjectionLike} destination Destination projection-like.\n * @return {import(\"./extent.js\").Extent} The transformed extent.\n * @api\n */\nexport function transformExtent(extent, source, destination) {\n var transformFunc = getTransform(source, destination);\n return applyTransform(extent, transformFunc);\n}\n/**\n * Transforms the given point to the destination projection.\n *\n * @param {import(\"./coordinate.js\").Coordinate} point Point.\n * @param {Projection} sourceProjection Source projection.\n * @param {Projection} destinationProjection Destination projection.\n * @return {import(\"./coordinate.js\").Coordinate} Point.\n */\nexport function transformWithProjections(point, sourceProjection, destinationProjection) {\n var transformFunc = getTransformFromProjections(sourceProjection, destinationProjection);\n return transformFunc(point);\n}\n/**\n * @type {?Projection}\n */\nvar userProjection = null;\n/**\n * Set the projection for coordinates supplied from and returned by API methods.\n * Note that this method is not yet a part of the stable API. Support for user\n * projections is not yet complete and should be considered experimental.\n * @param {ProjectionLike} projection The user projection.\n */\nexport function setUserProjection(projection) {\n userProjection = get(projection);\n}\n/**\n * Clear the user projection if set. Note that this method is not yet a part of\n * the stable API. Support for user projections is not yet complete and should\n * be considered experimental.\n */\nexport function clearUserProjection() {\n userProjection = null;\n}\n/**\n * Get the projection for coordinates supplied from and returned by API methods.\n * Note that this method is not yet a part of the stable API. Support for user\n * projections is not yet complete and should be considered experimental.\n * @returns {?Projection} The user projection (or null if not set).\n */\nexport function getUserProjection() {\n return userProjection;\n}\n/**\n * Use geographic coordinates (WGS-84 datum) in API methods. Note that this\n * method is not yet a part of the stable API. Support for user projections is\n * not yet complete and should be considered experimental.\n */\nexport function useGeographic() {\n setUserProjection('EPSG:4326');\n}\n/**\n * Return a coordinate transformed into the user projection. If no user projection\n * is set, the original coordinate is returned.\n * @param {Array} coordinate Input coordinate.\n * @param {ProjectionLike} sourceProjection The input coordinate projection.\n * @returns {Array} The input coordinate in the user projection.\n */\nexport function toUserCoordinate(coordinate, sourceProjection) {\n if (!userProjection) {\n return coordinate;\n }\n return transform(coordinate, sourceProjection, userProjection);\n}\n/**\n * Return a coordinate transformed from the user projection. If no user projection\n * is set, the original coordinate is returned.\n * @param {Array} coordinate Input coordinate.\n * @param {ProjectionLike} destProjection The destination projection.\n * @returns {Array} The input coordinate transformed.\n */\nexport function fromUserCoordinate(coordinate, destProjection) {\n if (!userProjection) {\n return coordinate;\n }\n return transform(coordinate, userProjection, destProjection);\n}\n/**\n * Return an extent transformed into the user projection. If no user projection\n * is set, the original extent is returned.\n * @param {import(\"./extent.js\").Extent} extent Input extent.\n * @param {ProjectionLike} sourceProjection The input extent projection.\n * @returns {import(\"./extent.js\").Extent} The input extent in the user projection.\n */\nexport function toUserExtent(extent, sourceProjection) {\n if (!userProjection) {\n return extent;\n }\n return transformExtent(extent, sourceProjection, userProjection);\n}\n/**\n * Return an extent transformed from the user projection. If no user projection\n * is set, the original extent is returned.\n * @param {import(\"./extent.js\").Extent} extent Input extent.\n * @param {ProjectionLike} destProjection The destination projection.\n * @returns {import(\"./extent.js\").Extent} The input extent transformed.\n */\nexport function fromUserExtent(extent, destProjection) {\n if (!userProjection) {\n return extent;\n }\n return transformExtent(extent, userProjection, destProjection);\n}\n/**\n * Add transforms to and from EPSG:4326 and EPSG:3857. This function is called\n * by when this module is executed and should only need to be called again after\n * `clearAllProjections()` is called (e.g. in tests).\n */\nexport function addCommon() {\n // Add transformations that don't alter coordinates to convert within set of\n // projections with equal meaning.\n addEquivalentProjections(EPSG3857_PROJECTIONS);\n addEquivalentProjections(EPSG4326_PROJECTIONS);\n // Add transformations to convert EPSG:4326 like coordinates to EPSG:3857 like\n // coordinates and back.\n addEquivalentTransforms(EPSG4326_PROJECTIONS, EPSG3857_PROJECTIONS, fromEPSG4326, toEPSG4326);\n}\naddCommon();\n//# sourceMappingURL=proj.js.map","/**\n * @module ol/transform\n */\nimport { assert } from './asserts.js';\n/**\n * An array representing an affine 2d transformation for use with\n * {@link module:ol/transform} functions. The array has 6 elements.\n * @typedef {!Array} Transform\n */\n/**\n * Collection of affine 2d transformation functions. The functions work on an\n * array of 6 elements. The element order is compatible with the [SVGMatrix\n * interface](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix) and is\n * a subset (elements a to f) of a 3×3 matrix:\n * ```\n * [ a c e ]\n * [ b d f ]\n * [ 0 0 1 ]\n * ```\n */\n/**\n * @private\n * @type {Transform}\n */\nvar tmp_ = new Array(6);\n/**\n * Create an identity transform.\n * @return {!Transform} Identity transform.\n */\nexport function create() {\n return [1, 0, 0, 1, 0, 0];\n}\n/**\n * Resets the given transform to an identity transform.\n * @param {!Transform} transform Transform.\n * @return {!Transform} Transform.\n */\nexport function reset(transform) {\n return set(transform, 1, 0, 0, 1, 0, 0);\n}\n/**\n * Multiply the underlying matrices of two transforms and return the result in\n * the first transform.\n * @param {!Transform} transform1 Transform parameters of matrix 1.\n * @param {!Transform} transform2 Transform parameters of matrix 2.\n * @return {!Transform} transform1 multiplied with transform2.\n */\nexport function multiply(transform1, transform2) {\n var a1 = transform1[0];\n var b1 = transform1[1];\n var c1 = transform1[2];\n var d1 = transform1[3];\n var e1 = transform1[4];\n var f1 = transform1[5];\n var a2 = transform2[0];\n var b2 = transform2[1];\n var c2 = transform2[2];\n var d2 = transform2[3];\n var e2 = transform2[4];\n var f2 = transform2[5];\n transform1[0] = a1 * a2 + c1 * b2;\n transform1[1] = b1 * a2 + d1 * b2;\n transform1[2] = a1 * c2 + c1 * d2;\n transform1[3] = b1 * c2 + d1 * d2;\n transform1[4] = a1 * e2 + c1 * f2 + e1;\n transform1[5] = b1 * e2 + d1 * f2 + f1;\n return transform1;\n}\n/**\n * Set the transform components a-f on a given transform.\n * @param {!Transform} transform Transform.\n * @param {number} a The a component of the transform.\n * @param {number} b The b component of the transform.\n * @param {number} c The c component of the transform.\n * @param {number} d The d component of the transform.\n * @param {number} e The e component of the transform.\n * @param {number} f The f component of the transform.\n * @return {!Transform} Matrix with transform applied.\n */\nexport function set(transform, a, b, c, d, e, f) {\n transform[0] = a;\n transform[1] = b;\n transform[2] = c;\n transform[3] = d;\n transform[4] = e;\n transform[5] = f;\n return transform;\n}\n/**\n * Set transform on one matrix from another matrix.\n * @param {!Transform} transform1 Matrix to set transform to.\n * @param {!Transform} transform2 Matrix to set transform from.\n * @return {!Transform} transform1 with transform from transform2 applied.\n */\nexport function setFromArray(transform1, transform2) {\n transform1[0] = transform2[0];\n transform1[1] = transform2[1];\n transform1[2] = transform2[2];\n transform1[3] = transform2[3];\n transform1[4] = transform2[4];\n transform1[5] = transform2[5];\n return transform1;\n}\n/**\n * Transforms the given coordinate with the given transform returning the\n * resulting, transformed coordinate. The coordinate will be modified in-place.\n *\n * @param {Transform} transform The transformation.\n * @param {import(\"./coordinate.js\").Coordinate|import(\"./pixel.js\").Pixel} coordinate The coordinate to transform.\n * @return {import(\"./coordinate.js\").Coordinate|import(\"./pixel.js\").Pixel} return coordinate so that operations can be\n * chained together.\n */\nexport function apply(transform, coordinate) {\n var x = coordinate[0];\n var y = coordinate[1];\n coordinate[0] = transform[0] * x + transform[2] * y + transform[4];\n coordinate[1] = transform[1] * x + transform[3] * y + transform[5];\n return coordinate;\n}\n/**\n * Applies rotation to the given transform.\n * @param {!Transform} transform Transform.\n * @param {number} angle Angle in radians.\n * @return {!Transform} The rotated transform.\n */\nexport function rotate(transform, angle) {\n var cos = Math.cos(angle);\n var sin = Math.sin(angle);\n return multiply(transform, set(tmp_, cos, sin, -sin, cos, 0, 0));\n}\n/**\n * Applies scale to a given transform.\n * @param {!Transform} transform Transform.\n * @param {number} x Scale factor x.\n * @param {number} y Scale factor y.\n * @return {!Transform} The scaled transform.\n */\nexport function scale(transform, x, y) {\n return multiply(transform, set(tmp_, x, 0, 0, y, 0, 0));\n}\n/**\n * Creates a scale transform.\n * @param {!Transform} target Transform to overwrite.\n * @param {number} x Scale factor x.\n * @param {number} y Scale factor y.\n * @return {!Transform} The scale transform.\n */\nexport function makeScale(target, x, y) {\n return set(target, x, 0, 0, y, 0, 0);\n}\n/**\n * Applies translation to the given transform.\n * @param {!Transform} transform Transform.\n * @param {number} dx Translation x.\n * @param {number} dy Translation y.\n * @return {!Transform} The translated transform.\n */\nexport function translate(transform, dx, dy) {\n return multiply(transform, set(tmp_, 1, 0, 0, 1, dx, dy));\n}\n/**\n * Creates a composite transform given an initial translation, scale, rotation, and\n * final translation (in that order only, not commutative).\n * @param {!Transform} transform The transform (will be modified in place).\n * @param {number} dx1 Initial translation x.\n * @param {number} dy1 Initial translation y.\n * @param {number} sx Scale factor x.\n * @param {number} sy Scale factor y.\n * @param {number} angle Rotation (in counter-clockwise radians).\n * @param {number} dx2 Final translation x.\n * @param {number} dy2 Final translation y.\n * @return {!Transform} The composite transform.\n */\nexport function compose(transform, dx1, dy1, sx, sy, angle, dx2, dy2) {\n var sin = Math.sin(angle);\n var cos = Math.cos(angle);\n transform[0] = sx * cos;\n transform[1] = sy * sin;\n transform[2] = -sx * sin;\n transform[3] = sy * cos;\n transform[4] = dx2 * sx * cos - dy2 * sx * sin + dx1;\n transform[5] = dx2 * sy * sin + dy2 * sy * cos + dy1;\n return transform;\n}\n/**\n * Invert the given transform.\n * @param {!Transform} source The source transform to invert.\n * @return {!Transform} The inverted (source) transform.\n */\nexport function invert(source) {\n return makeInverse(source, source);\n}\n/**\n * Invert the given transform.\n * @param {!Transform} target Transform to be set as the inverse of\n * the source transform.\n * @param {!Transform} source The source transform to invert.\n * @return {!Transform} The inverted (target) transform.\n */\nexport function makeInverse(target, source) {\n var det = determinant(source);\n assert(det !== 0, 32); // Transformation matrix cannot be inverted\n var a = source[0];\n var b = source[1];\n var c = source[2];\n var d = source[3];\n var e = source[4];\n var f = source[5];\n target[0] = d / det;\n target[1] = -b / det;\n target[2] = -c / det;\n target[3] = a / det;\n target[4] = (c * f - d * e) / det;\n target[5] = -(a * f - b * e) / det;\n return target;\n}\n/**\n * Returns the determinant of the given matrix.\n * @param {!Transform} mat Matrix.\n * @return {number} Determinant.\n */\nexport function determinant(mat) {\n return mat[0] * mat[3] - mat[1] * mat[2];\n}\n/**\n * A string version of the transform. This can be used\n * for CSS transforms.\n * @param {!Transform} mat Matrix.\n * @return {string} The transform as a string.\n */\nexport function toString(mat) {\n return 'matrix(' + mat.join(', ') + ')';\n}\n//# sourceMappingURL=transform.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/geom/Geometry\n */\nimport { abstract } from '../util.js';\nimport BaseObject from '../Object.js';\nimport { createEmpty, getHeight, returnOrUpdate } from '../extent.js';\nimport { transform2D } from './flat/transform.js';\nimport { get as getProjection, getTransform } from '../proj.js';\nimport Units from '../proj/Units.js';\nimport { create as createTransform, compose as composeTransform } from '../transform.js';\nimport { memoizeOne } from '../functions.js';\n/**\n * @type {import(\"../transform.js\").Transform}\n */\nvar tmpTransform = createTransform();\n/**\n * @classdesc\n * Abstract base class; normally only used for creating subclasses and not\n * instantiated in apps.\n * Base class for vector geometries.\n *\n * To get notified of changes to the geometry, register a listener for the\n * generic `change` event on your geometry instance.\n *\n * @abstract\n * @api\n */\nvar Geometry = /** @class */ (function (_super) {\n __extends(Geometry, _super);\n function Geometry() {\n var _this = _super.call(this) || this;\n /**\n * @private\n * @type {import(\"../extent.js\").Extent}\n */\n _this.extent_ = createEmpty();\n /**\n * @private\n * @type {number}\n */\n _this.extentRevision_ = -1;\n /**\n * @protected\n * @type {number}\n */\n _this.simplifiedGeometryMaxMinSquaredTolerance = 0;\n /**\n * @protected\n * @type {number}\n */\n _this.simplifiedGeometryRevision = 0;\n /**\n * Get a transformed and simplified version of the geometry.\n * @abstract\n * @param {number} revision The geometry revision.\n * @param {number} squaredTolerance Squared tolerance.\n * @param {import(\"../proj.js\").TransformFunction} [opt_transform] Optional transform function.\n * @return {Geometry} Simplified geometry.\n */\n _this.simplifyTransformedInternal = memoizeOne(function (revision, squaredTolerance, opt_transform) {\n if (!opt_transform) {\n return this.getSimplifiedGeometry(squaredTolerance);\n }\n var clone = this.clone();\n clone.applyTransform(opt_transform);\n return clone.getSimplifiedGeometry(squaredTolerance);\n });\n return _this;\n }\n /**\n * Get a transformed and simplified version of the geometry.\n * @abstract\n * @param {number} squaredTolerance Squared tolerance.\n * @param {import(\"../proj.js\").TransformFunction} [opt_transform] Optional transform function.\n * @return {Geometry} Simplified geometry.\n */\n Geometry.prototype.simplifyTransformed = function (squaredTolerance, opt_transform) {\n return this.simplifyTransformedInternal(this.getRevision(), squaredTolerance, opt_transform);\n };\n /**\n * Make a complete copy of the geometry.\n * @abstract\n * @return {!Geometry} Clone.\n */\n Geometry.prototype.clone = function () {\n return abstract();\n };\n /**\n * @abstract\n * @param {number} x X.\n * @param {number} y Y.\n * @param {import(\"../coordinate.js\").Coordinate} closestPoint Closest point.\n * @param {number} minSquaredDistance Minimum squared distance.\n * @return {number} Minimum squared distance.\n */\n Geometry.prototype.closestPointXY = function (x, y, closestPoint, minSquaredDistance) {\n return abstract();\n };\n /**\n * @param {number} x X.\n * @param {number} y Y.\n * @return {boolean} Contains (x, y).\n */\n Geometry.prototype.containsXY = function (x, y) {\n var coord = this.getClosestPoint([x, y]);\n return coord[0] === x && coord[1] === y;\n };\n /**\n * Return the closest point of the geometry to the passed point as\n * {@link module:ol/coordinate~Coordinate coordinate}.\n * @param {import(\"../coordinate.js\").Coordinate} point Point.\n * @param {import(\"../coordinate.js\").Coordinate=} opt_closestPoint Closest point.\n * @return {import(\"../coordinate.js\").Coordinate} Closest point.\n * @api\n */\n Geometry.prototype.getClosestPoint = function (point, opt_closestPoint) {\n var closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN];\n this.closestPointXY(point[0], point[1], closestPoint, Infinity);\n return closestPoint;\n };\n /**\n * Returns true if this geometry includes the specified coordinate. If the\n * coordinate is on the boundary of the geometry, returns false.\n * @param {import(\"../coordinate.js\").Coordinate} coordinate Coordinate.\n * @return {boolean} Contains coordinate.\n * @api\n */\n Geometry.prototype.intersectsCoordinate = function (coordinate) {\n return this.containsXY(coordinate[0], coordinate[1]);\n };\n /**\n * @abstract\n * @param {import(\"../extent.js\").Extent} extent Extent.\n * @protected\n * @return {import(\"../extent.js\").Extent} extent Extent.\n */\n Geometry.prototype.computeExtent = function (extent) {\n return abstract();\n };\n /**\n * Get the extent of the geometry.\n * @param {import(\"../extent.js\").Extent=} opt_extent Extent.\n * @return {import(\"../extent.js\").Extent} extent Extent.\n * @api\n */\n Geometry.prototype.getExtent = function (opt_extent) {\n if (this.extentRevision_ != this.getRevision()) {\n this.extent_ = this.computeExtent(this.extent_);\n this.extentRevision_ = this.getRevision();\n }\n return returnOrUpdate(this.extent_, opt_extent);\n };\n /**\n * Rotate the geometry around a given coordinate. This modifies the geometry\n * coordinates in place.\n * @abstract\n * @param {number} angle Rotation angle in radians.\n * @param {import(\"../coordinate.js\").Coordinate} anchor The rotation center.\n * @api\n */\n Geometry.prototype.rotate = function (angle, anchor) {\n abstract();\n };\n /**\n * Scale the geometry (with an optional origin). This modifies the geometry\n * coordinates in place.\n * @abstract\n * @param {number} sx The scaling factor in the x-direction.\n * @param {number=} opt_sy The scaling factor in the y-direction (defaults to\n * sx).\n * @param {import(\"../coordinate.js\").Coordinate=} opt_anchor The scale origin (defaults to the center\n * of the geometry extent).\n * @api\n */\n Geometry.prototype.scale = function (sx, opt_sy, opt_anchor) {\n abstract();\n };\n /**\n * Create a simplified version of this geometry. For linestrings, this uses\n * the [Douglas Peucker](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm)\n * algorithm. For polygons, a quantization-based\n * simplification is used to preserve topology.\n * @param {number} tolerance The tolerance distance for simplification.\n * @return {Geometry} A new, simplified version of the original geometry.\n * @api\n */\n Geometry.prototype.simplify = function (tolerance) {\n return this.getSimplifiedGeometry(tolerance * tolerance);\n };\n /**\n * Create a simplified version of this geometry using the Douglas Peucker\n * algorithm.\n * See https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm.\n * @abstract\n * @param {number} squaredTolerance Squared tolerance.\n * @return {Geometry} Simplified geometry.\n */\n Geometry.prototype.getSimplifiedGeometry = function (squaredTolerance) {\n return abstract();\n };\n /**\n * Get the type of this geometry.\n * @abstract\n * @return {import(\"./GeometryType.js\").default} Geometry type.\n */\n Geometry.prototype.getType = function () {\n return abstract();\n };\n /**\n * Apply a transform function to the coordinates of the geometry.\n * The geometry is modified in place.\n * If you do not want the geometry modified in place, first `clone()` it and\n * then use this function on the clone.\n * @abstract\n * @param {import(\"../proj.js\").TransformFunction} transformFn Transform function.\n * Called with a flat array of geometry coordinates.\n */\n Geometry.prototype.applyTransform = function (transformFn) {\n abstract();\n };\n /**\n * Test if the geometry and the passed extent intersect.\n * @abstract\n * @param {import(\"../extent.js\").Extent} extent Extent.\n * @return {boolean} `true` if the geometry and the extent intersect.\n */\n Geometry.prototype.intersectsExtent = function (extent) {\n return abstract();\n };\n /**\n * Translate the geometry. This modifies the geometry coordinates in place. If\n * instead you want a new geometry, first `clone()` this geometry.\n * @abstract\n * @param {number} deltaX Delta X.\n * @param {number} deltaY Delta Y.\n * @api\n */\n Geometry.prototype.translate = function (deltaX, deltaY) {\n abstract();\n };\n /**\n * Transform each coordinate of the geometry from one coordinate reference\n * system to another. The geometry is modified in place.\n * For example, a line will be transformed to a line and a circle to a circle.\n * If you do not want the geometry modified in place, first `clone()` it and\n * then use this function on the clone.\n *\n * @param {import(\"../proj.js\").ProjectionLike} source The current projection. Can be a\n * string identifier or a {@link module:ol/proj/Projection~Projection} object.\n * @param {import(\"../proj.js\").ProjectionLike} destination The desired projection. Can be a\n * string identifier or a {@link module:ol/proj/Projection~Projection} object.\n * @return {Geometry} This geometry. Note that original geometry is\n * modified in place.\n * @api\n */\n Geometry.prototype.transform = function (source, destination) {\n /** @type {import(\"../proj/Projection.js\").default} */\n var sourceProj = getProjection(source);\n var transformFn = sourceProj.getUnits() == Units.TILE_PIXELS ?\n function (inCoordinates, outCoordinates, stride) {\n var pixelExtent = sourceProj.getExtent();\n var projectedExtent = sourceProj.getWorldExtent();\n var scale = getHeight(projectedExtent) / getHeight(pixelExtent);\n composeTransform(tmpTransform, projectedExtent[0], projectedExtent[3], scale, -scale, 0, 0, 0);\n transform2D(inCoordinates, 0, inCoordinates.length, stride, tmpTransform, outCoordinates);\n return getTransform(sourceProj, destination)(inCoordinates, outCoordinates, stride);\n } :\n getTransform(sourceProj, destination);\n this.applyTransform(transformFn);\n return this;\n };\n return Geometry;\n}(BaseObject));\nexport default Geometry;\n//# sourceMappingURL=Geometry.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/geom/SimpleGeometry\n */\nimport { abstract } from '../util.js';\nimport { createOrUpdateFromFlatCoordinates, getCenter } from '../extent.js';\nimport Geometry from './Geometry.js';\nimport GeometryLayout from './GeometryLayout.js';\nimport { rotate, scale, translate, transform2D } from './flat/transform.js';\n/**\n * @classdesc\n * Abstract base class; only used for creating subclasses; do not instantiate\n * in apps, as cannot be rendered.\n *\n * @abstract\n * @api\n */\nvar SimpleGeometry = /** @class */ (function (_super) {\n __extends(SimpleGeometry, _super);\n function SimpleGeometry() {\n var _this = _super.call(this) || this;\n /**\n * @protected\n * @type {GeometryLayout}\n */\n _this.layout = GeometryLayout.XY;\n /**\n * @protected\n * @type {number}\n */\n _this.stride = 2;\n /**\n * @protected\n * @type {Array}\n */\n _this.flatCoordinates = null;\n return _this;\n }\n /**\n * @inheritDoc\n */\n SimpleGeometry.prototype.computeExtent = function (extent) {\n return createOrUpdateFromFlatCoordinates(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, extent);\n };\n /**\n * @abstract\n * @return {Array<*>} Coordinates.\n */\n SimpleGeometry.prototype.getCoordinates = function () {\n return abstract();\n };\n /**\n * Return the first coordinate of the geometry.\n * @return {import(\"../coordinate.js\").Coordinate} First coordinate.\n * @api\n */\n SimpleGeometry.prototype.getFirstCoordinate = function () {\n return this.flatCoordinates.slice(0, this.stride);\n };\n /**\n * @return {Array} Flat coordinates.\n */\n SimpleGeometry.prototype.getFlatCoordinates = function () {\n return this.flatCoordinates;\n };\n /**\n * Return the last coordinate of the geometry.\n * @return {import(\"../coordinate.js\").Coordinate} Last point.\n * @api\n */\n SimpleGeometry.prototype.getLastCoordinate = function () {\n return this.flatCoordinates.slice(this.flatCoordinates.length - this.stride);\n };\n /**\n * Return the {@link module:ol/geom/GeometryLayout layout} of the geometry.\n * @return {GeometryLayout} Layout.\n * @api\n */\n SimpleGeometry.prototype.getLayout = function () {\n return this.layout;\n };\n /**\n * @inheritDoc\n */\n SimpleGeometry.prototype.getSimplifiedGeometry = function (squaredTolerance) {\n if (this.simplifiedGeometryRevision !== this.getRevision()) {\n this.simplifiedGeometryMaxMinSquaredTolerance = 0;\n this.simplifiedGeometryRevision = this.getRevision();\n }\n // If squaredTolerance is negative or if we know that simplification will not\n // have any effect then just return this.\n if (squaredTolerance < 0 ||\n (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 &&\n squaredTolerance <= this.simplifiedGeometryMaxMinSquaredTolerance)) {\n return this;\n }\n var simplifiedGeometry = this.getSimplifiedGeometryInternal(squaredTolerance);\n var simplifiedFlatCoordinates = simplifiedGeometry.getFlatCoordinates();\n if (simplifiedFlatCoordinates.length < this.flatCoordinates.length) {\n return simplifiedGeometry;\n }\n else {\n // Simplification did not actually remove any coordinates. We now know\n // that any calls to getSimplifiedGeometry with a squaredTolerance less\n // than or equal to the current squaredTolerance will also not have any\n // effect. This allows us to short circuit simplification (saving CPU\n // cycles) and prevents the cache of simplified geometries from filling\n // up with useless identical copies of this geometry (saving memory).\n this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance;\n return this;\n }\n };\n /**\n * @param {number} squaredTolerance Squared tolerance.\n * @return {SimpleGeometry} Simplified geometry.\n * @protected\n */\n SimpleGeometry.prototype.getSimplifiedGeometryInternal = function (squaredTolerance) {\n return this;\n };\n /**\n * @return {number} Stride.\n */\n SimpleGeometry.prototype.getStride = function () {\n return this.stride;\n };\n /**\n * @param {GeometryLayout} layout Layout.\n * @param {Array} flatCoordinates Flat coordinates.\n */\n SimpleGeometry.prototype.setFlatCoordinates = function (layout, flatCoordinates) {\n this.stride = getStrideForLayout(layout);\n this.layout = layout;\n this.flatCoordinates = flatCoordinates;\n };\n /**\n * @abstract\n * @param {!Array<*>} coordinates Coordinates.\n * @param {GeometryLayout=} opt_layout Layout.\n */\n SimpleGeometry.prototype.setCoordinates = function (coordinates, opt_layout) {\n abstract();\n };\n /**\n * @param {GeometryLayout|undefined} layout Layout.\n * @param {Array<*>} coordinates Coordinates.\n * @param {number} nesting Nesting.\n * @protected\n */\n SimpleGeometry.prototype.setLayout = function (layout, coordinates, nesting) {\n /** @type {number} */\n var stride;\n if (layout) {\n stride = getStrideForLayout(layout);\n }\n else {\n for (var i = 0; i < nesting; ++i) {\n if (coordinates.length === 0) {\n this.layout = GeometryLayout.XY;\n this.stride = 2;\n return;\n }\n else {\n coordinates = /** @type {Array} */ (coordinates[0]);\n }\n }\n stride = coordinates.length;\n layout = getLayoutForStride(stride);\n }\n this.layout = layout;\n this.stride = stride;\n };\n /**\n * Apply a transform function to the coordinates of the geometry.\n * The geometry is modified in place.\n * If you do not want the geometry modified in place, first `clone()` it and\n * then use this function on the clone.\n * @param {import(\"../proj.js\").TransformFunction} transformFn Transform function.\n * Called with a flat array of geometry coordinates.\n * @api\n */\n SimpleGeometry.prototype.applyTransform = function (transformFn) {\n if (this.flatCoordinates) {\n transformFn(this.flatCoordinates, this.flatCoordinates, this.stride);\n this.changed();\n }\n };\n /**\n * Rotate the geometry around a given coordinate. This modifies the geometry\n * coordinates in place.\n * @param {number} angle Rotation angle in radians.\n * @param {import(\"../coordinate.js\").Coordinate} anchor The rotation center.\n * @api\n */\n SimpleGeometry.prototype.rotate = function (angle, anchor) {\n var flatCoordinates = this.getFlatCoordinates();\n if (flatCoordinates) {\n var stride = this.getStride();\n rotate(flatCoordinates, 0, flatCoordinates.length, stride, angle, anchor, flatCoordinates);\n this.changed();\n }\n };\n /**\n * Scale the geometry (with an optional origin). This modifies the geometry\n * coordinates in place.\n * @param {number} sx The scaling factor in the x-direction.\n * @param {number=} opt_sy The scaling factor in the y-direction (defaults to\n * sx).\n * @param {import(\"../coordinate.js\").Coordinate=} opt_anchor The scale origin (defaults to the center\n * of the geometry extent).\n * @api\n */\n SimpleGeometry.prototype.scale = function (sx, opt_sy, opt_anchor) {\n var sy = opt_sy;\n if (sy === undefined) {\n sy = sx;\n }\n var anchor = opt_anchor;\n if (!anchor) {\n anchor = getCenter(this.getExtent());\n }\n var flatCoordinates = this.getFlatCoordinates();\n if (flatCoordinates) {\n var stride = this.getStride();\n scale(flatCoordinates, 0, flatCoordinates.length, stride, sx, sy, anchor, flatCoordinates);\n this.changed();\n }\n };\n /**\n * Translate the geometry. This modifies the geometry coordinates in place. If\n * instead you want a new geometry, first `clone()` this geometry.\n * @param {number} deltaX Delta X.\n * @param {number} deltaY Delta Y.\n * @api\n */\n SimpleGeometry.prototype.translate = function (deltaX, deltaY) {\n var flatCoordinates = this.getFlatCoordinates();\n if (flatCoordinates) {\n var stride = this.getStride();\n translate(flatCoordinates, 0, flatCoordinates.length, stride, deltaX, deltaY, flatCoordinates);\n this.changed();\n }\n };\n return SimpleGeometry;\n}(Geometry));\n/**\n * @param {number} stride Stride.\n * @return {GeometryLayout} layout Layout.\n */\nfunction getLayoutForStride(stride) {\n var layout;\n if (stride == 2) {\n layout = GeometryLayout.XY;\n }\n else if (stride == 3) {\n layout = GeometryLayout.XYZ;\n }\n else if (stride == 4) {\n layout = GeometryLayout.XYZM;\n }\n return (\n /** @type {GeometryLayout} */ (layout));\n}\n/**\n * @param {GeometryLayout} layout Layout.\n * @return {number} Stride.\n */\nexport function getStrideForLayout(layout) {\n var stride;\n if (layout == GeometryLayout.XY) {\n stride = 2;\n }\n else if (layout == GeometryLayout.XYZ || layout == GeometryLayout.XYM) {\n stride = 3;\n }\n else if (layout == GeometryLayout.XYZM) {\n stride = 4;\n }\n return /** @type {number} */ (stride);\n}\n/**\n * @param {SimpleGeometry} simpleGeometry Simple geometry.\n * @param {import(\"../transform.js\").Transform} transform Transform.\n * @param {Array=} opt_dest Destination.\n * @return {Array} Transformed flat coordinates.\n */\nexport function transformGeom2D(simpleGeometry, transform, opt_dest) {\n var flatCoordinates = simpleGeometry.getFlatCoordinates();\n if (!flatCoordinates) {\n return null;\n }\n else {\n var stride = simpleGeometry.getStride();\n return transform2D(flatCoordinates, 0, flatCoordinates.length, stride, transform, opt_dest);\n }\n}\nexport default SimpleGeometry;\n//# sourceMappingURL=SimpleGeometry.js.map","/**\n * @module ol/geom/flat/area\n */\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @return {number} Area.\n */\nexport function linearRing(flatCoordinates, offset, end, stride) {\n var twiceArea = 0;\n var x1 = flatCoordinates[end - stride];\n var y1 = flatCoordinates[end - stride + 1];\n for (; offset < end; offset += stride) {\n var x2 = flatCoordinates[offset];\n var y2 = flatCoordinates[offset + 1];\n twiceArea += y1 * x2 - x1 * y2;\n x1 = x2;\n y1 = y2;\n }\n return twiceArea / 2;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @return {number} Area.\n */\nexport function linearRings(flatCoordinates, offset, ends, stride) {\n var area = 0;\n for (var i = 0, ii = ends.length; i < ii; ++i) {\n var end = ends[i];\n area += linearRing(flatCoordinates, offset, end, stride);\n offset = end;\n }\n return area;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Endss.\n * @param {number} stride Stride.\n * @return {number} Area.\n */\nexport function linearRingss(flatCoordinates, offset, endss, stride) {\n var area = 0;\n for (var i = 0, ii = endss.length; i < ii; ++i) {\n var ends = endss[i];\n area += linearRings(flatCoordinates, offset, ends, stride);\n offset = ends[ends.length - 1];\n }\n return area;\n}\n//# sourceMappingURL=area.js.map","/**\n * @module ol/geom/flat/closest\n */\nimport { lerp, squaredDistance as squaredDx } from '../../math.js';\n/**\n * Returns the point on the 2D line segment flatCoordinates[offset1] to\n * flatCoordinates[offset2] that is closest to the point (x, y). Extra\n * dimensions are linearly interpolated.\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset1 Offset 1.\n * @param {number} offset2 Offset 2.\n * @param {number} stride Stride.\n * @param {number} x X.\n * @param {number} y Y.\n * @param {Array} closestPoint Closest point.\n */\nfunction assignClosest(flatCoordinates, offset1, offset2, stride, x, y, closestPoint) {\n var x1 = flatCoordinates[offset1];\n var y1 = flatCoordinates[offset1 + 1];\n var dx = flatCoordinates[offset2] - x1;\n var dy = flatCoordinates[offset2 + 1] - y1;\n var offset;\n if (dx === 0 && dy === 0) {\n offset = offset1;\n }\n else {\n var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);\n if (t > 1) {\n offset = offset2;\n }\n else if (t > 0) {\n for (var i = 0; i < stride; ++i) {\n closestPoint[i] = lerp(flatCoordinates[offset1 + i], flatCoordinates[offset2 + i], t);\n }\n closestPoint.length = stride;\n return;\n }\n else {\n offset = offset1;\n }\n }\n for (var i = 0; i < stride; ++i) {\n closestPoint[i] = flatCoordinates[offset + i];\n }\n closestPoint.length = stride;\n}\n/**\n * Return the squared of the largest distance between any pair of consecutive\n * coordinates.\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {number} max Max squared delta.\n * @return {number} Max squared delta.\n */\nexport function maxSquaredDelta(flatCoordinates, offset, end, stride, max) {\n var x1 = flatCoordinates[offset];\n var y1 = flatCoordinates[offset + 1];\n for (offset += stride; offset < end; offset += stride) {\n var x2 = flatCoordinates[offset];\n var y2 = flatCoordinates[offset + 1];\n var squaredDelta = squaredDx(x1, y1, x2, y2);\n if (squaredDelta > max) {\n max = squaredDelta;\n }\n x1 = x2;\n y1 = y2;\n }\n return max;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @param {number} max Max squared delta.\n * @return {number} Max squared delta.\n */\nexport function arrayMaxSquaredDelta(flatCoordinates, offset, ends, stride, max) {\n for (var i = 0, ii = ends.length; i < ii; ++i) {\n var end = ends[i];\n max = maxSquaredDelta(flatCoordinates, offset, end, stride, max);\n offset = end;\n }\n return max;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Endss.\n * @param {number} stride Stride.\n * @param {number} max Max squared delta.\n * @return {number} Max squared delta.\n */\nexport function multiArrayMaxSquaredDelta(flatCoordinates, offset, endss, stride, max) {\n for (var i = 0, ii = endss.length; i < ii; ++i) {\n var ends = endss[i];\n max = arrayMaxSquaredDelta(flatCoordinates, offset, ends, stride, max);\n offset = ends[ends.length - 1];\n }\n return max;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {number} maxDelta Max delta.\n * @param {boolean} isRing Is ring.\n * @param {number} x X.\n * @param {number} y Y.\n * @param {Array} closestPoint Closest point.\n * @param {number} minSquaredDistance Minimum squared distance.\n * @param {Array=} opt_tmpPoint Temporary point object.\n * @return {number} Minimum squared distance.\n */\nexport function assignClosestPoint(flatCoordinates, offset, end, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, opt_tmpPoint) {\n if (offset == end) {\n return minSquaredDistance;\n }\n var i, squaredDistance;\n if (maxDelta === 0) {\n // All points are identical, so just test the first point.\n squaredDistance = squaredDx(x, y, flatCoordinates[offset], flatCoordinates[offset + 1]);\n if (squaredDistance < minSquaredDistance) {\n for (i = 0; i < stride; ++i) {\n closestPoint[i] = flatCoordinates[offset + i];\n }\n closestPoint.length = stride;\n return squaredDistance;\n }\n else {\n return minSquaredDistance;\n }\n }\n var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN];\n var index = offset + stride;\n while (index < end) {\n assignClosest(flatCoordinates, index - stride, index, stride, x, y, tmpPoint);\n squaredDistance = squaredDx(x, y, tmpPoint[0], tmpPoint[1]);\n if (squaredDistance < minSquaredDistance) {\n minSquaredDistance = squaredDistance;\n for (i = 0; i < stride; ++i) {\n closestPoint[i] = tmpPoint[i];\n }\n closestPoint.length = stride;\n index += stride;\n }\n else {\n // Skip ahead multiple points, because we know that all the skipped\n // points cannot be any closer than the closest point we have found so\n // far. We know this because we know how close the current point is, how\n // close the closest point we have found so far is, and the maximum\n // distance between consecutive points. For example, if we're currently\n // at distance 10, the best we've found so far is 3, and that the maximum\n // distance between consecutive points is 2, then we'll need to skip at\n // least (10 - 3) / 2 == 3 (rounded down) points to have any chance of\n // finding a closer point. We use Math.max(..., 1) to ensure that we\n // always advance at least one point, to avoid an infinite loop.\n index += stride * Math.max(((Math.sqrt(squaredDistance) -\n Math.sqrt(minSquaredDistance)) / maxDelta) | 0, 1);\n }\n }\n if (isRing) {\n // Check the closing segment.\n assignClosest(flatCoordinates, end - stride, offset, stride, x, y, tmpPoint);\n squaredDistance = squaredDx(x, y, tmpPoint[0], tmpPoint[1]);\n if (squaredDistance < minSquaredDistance) {\n minSquaredDistance = squaredDistance;\n for (i = 0; i < stride; ++i) {\n closestPoint[i] = tmpPoint[i];\n }\n closestPoint.length = stride;\n }\n }\n return minSquaredDistance;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @param {number} maxDelta Max delta.\n * @param {boolean} isRing Is ring.\n * @param {number} x X.\n * @param {number} y Y.\n * @param {Array} closestPoint Closest point.\n * @param {number} minSquaredDistance Minimum squared distance.\n * @param {Array=} opt_tmpPoint Temporary point object.\n * @return {number} Minimum squared distance.\n */\nexport function assignClosestArrayPoint(flatCoordinates, offset, ends, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, opt_tmpPoint) {\n var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN];\n for (var i = 0, ii = ends.length; i < ii; ++i) {\n var end = ends[i];\n minSquaredDistance = assignClosestPoint(flatCoordinates, offset, end, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint);\n offset = end;\n }\n return minSquaredDistance;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Endss.\n * @param {number} stride Stride.\n * @param {number} maxDelta Max delta.\n * @param {boolean} isRing Is ring.\n * @param {number} x X.\n * @param {number} y Y.\n * @param {Array} closestPoint Closest point.\n * @param {number} minSquaredDistance Minimum squared distance.\n * @param {Array=} opt_tmpPoint Temporary point object.\n * @return {number} Minimum squared distance.\n */\nexport function assignClosestMultiArrayPoint(flatCoordinates, offset, endss, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, opt_tmpPoint) {\n var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN];\n for (var i = 0, ii = endss.length; i < ii; ++i) {\n var ends = endss[i];\n minSquaredDistance = assignClosestArrayPoint(flatCoordinates, offset, ends, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint);\n offset = ends[ends.length - 1];\n }\n return minSquaredDistance;\n}\n//# sourceMappingURL=closest.js.map","/**\n * @module ol/geom/flat/deflate\n */\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {import(\"../../coordinate.js\").Coordinate} coordinate Coordinate.\n * @param {number} stride Stride.\n * @return {number} offset Offset.\n */\nexport function deflateCoordinate(flatCoordinates, offset, coordinate, stride) {\n for (var i = 0, ii = coordinate.length; i < ii; ++i) {\n flatCoordinates[offset++] = coordinate[i];\n }\n return offset;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} coordinates Coordinates.\n * @param {number} stride Stride.\n * @return {number} offset Offset.\n */\nexport function deflateCoordinates(flatCoordinates, offset, coordinates, stride) {\n for (var i = 0, ii = coordinates.length; i < ii; ++i) {\n var coordinate = coordinates[i];\n for (var j = 0; j < stride; ++j) {\n flatCoordinates[offset++] = coordinate[j];\n }\n }\n return offset;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} coordinatess Coordinatess.\n * @param {number} stride Stride.\n * @param {Array=} opt_ends Ends.\n * @return {Array} Ends.\n */\nexport function deflateCoordinatesArray(flatCoordinates, offset, coordinatess, stride, opt_ends) {\n var ends = opt_ends ? opt_ends : [];\n var i = 0;\n for (var j = 0, jj = coordinatess.length; j < jj; ++j) {\n var end = deflateCoordinates(flatCoordinates, offset, coordinatess[j], stride);\n ends[i++] = end;\n offset = end;\n }\n ends.length = i;\n return ends;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>>} coordinatesss Coordinatesss.\n * @param {number} stride Stride.\n * @param {Array>=} opt_endss Endss.\n * @return {Array>} Endss.\n */\nexport function deflateMultiCoordinatesArray(flatCoordinates, offset, coordinatesss, stride, opt_endss) {\n var endss = opt_endss ? opt_endss : [];\n var i = 0;\n for (var j = 0, jj = coordinatesss.length; j < jj; ++j) {\n var ends = deflateCoordinatesArray(flatCoordinates, offset, coordinatesss[j], stride, endss[i]);\n endss[i++] = ends;\n offset = ends[ends.length - 1];\n }\n endss.length = i;\n return endss;\n}\n//# sourceMappingURL=deflate.js.map","/**\n * @module ol/geom/flat/inflate\n */\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {Array=} opt_coordinates Coordinates.\n * @return {Array} Coordinates.\n */\nexport function inflateCoordinates(flatCoordinates, offset, end, stride, opt_coordinates) {\n var coordinates = opt_coordinates !== undefined ? opt_coordinates : [];\n var i = 0;\n for (var j = offset; j < end; j += stride) {\n coordinates[i++] = flatCoordinates.slice(j, j + stride);\n }\n coordinates.length = i;\n return coordinates;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @param {Array>=} opt_coordinatess Coordinatess.\n * @return {Array>} Coordinatess.\n */\nexport function inflateCoordinatesArray(flatCoordinates, offset, ends, stride, opt_coordinatess) {\n var coordinatess = opt_coordinatess !== undefined ? opt_coordinatess : [];\n var i = 0;\n for (var j = 0, jj = ends.length; j < jj; ++j) {\n var end = ends[j];\n coordinatess[i++] = inflateCoordinates(flatCoordinates, offset, end, stride, coordinatess[i]);\n offset = end;\n }\n coordinatess.length = i;\n return coordinatess;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Endss.\n * @param {number} stride Stride.\n * @param {Array>>=} opt_coordinatesss\n * Coordinatesss.\n * @return {Array>>} Coordinatesss.\n */\nexport function inflateMultiCoordinatesArray(flatCoordinates, offset, endss, stride, opt_coordinatesss) {\n var coordinatesss = opt_coordinatesss !== undefined ? opt_coordinatesss : [];\n var i = 0;\n for (var j = 0, jj = endss.length; j < jj; ++j) {\n var ends = endss[j];\n coordinatesss[i++] = inflateCoordinatesArray(flatCoordinates, offset, ends, stride, coordinatesss[i]);\n offset = ends[ends.length - 1];\n }\n coordinatesss.length = i;\n return coordinatesss;\n}\n//# sourceMappingURL=inflate.js.map","/**\n * @module ol/geom/flat/simplify\n */\n// Based on simplify-js https://github.com/mourner/simplify-js\n// Copyright (c) 2012, Vladimir Agafonkin\n// All rights reserved.\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are met:\n//\n// 1. Redistributions of source code must retain the above copyright notice,\n// this list of conditions and the following disclaimer.\n//\n// 2. Redistributions in binary form must reproduce the above copyright\n// notice, this list of conditions and the following disclaimer in the\n// documentation and/or other materials provided with the distribution.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n// POSSIBILITY OF SUCH DAMAGE.\nimport { squaredSegmentDistance, squaredDistance } from '../../math.js';\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {number} squaredTolerance Squared tolerance.\n * @param {boolean} highQuality Highest quality.\n * @param {Array=} opt_simplifiedFlatCoordinates Simplified flat\n * coordinates.\n * @return {Array} Simplified line string.\n */\nexport function simplifyLineString(flatCoordinates, offset, end, stride, squaredTolerance, highQuality, opt_simplifiedFlatCoordinates) {\n var simplifiedFlatCoordinates = opt_simplifiedFlatCoordinates !== undefined ?\n opt_simplifiedFlatCoordinates : [];\n if (!highQuality) {\n end = radialDistance(flatCoordinates, offset, end, stride, squaredTolerance, simplifiedFlatCoordinates, 0);\n flatCoordinates = simplifiedFlatCoordinates;\n offset = 0;\n stride = 2;\n }\n simplifiedFlatCoordinates.length = douglasPeucker(flatCoordinates, offset, end, stride, squaredTolerance, simplifiedFlatCoordinates, 0);\n return simplifiedFlatCoordinates;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {number} squaredTolerance Squared tolerance.\n * @param {Array} simplifiedFlatCoordinates Simplified flat\n * coordinates.\n * @param {number} simplifiedOffset Simplified offset.\n * @return {number} Simplified offset.\n */\nexport function douglasPeucker(flatCoordinates, offset, end, stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) {\n var n = (end - offset) / stride;\n if (n < 3) {\n for (; offset < end; offset += stride) {\n simplifiedFlatCoordinates[simplifiedOffset++] =\n flatCoordinates[offset];\n simplifiedFlatCoordinates[simplifiedOffset++] =\n flatCoordinates[offset + 1];\n }\n return simplifiedOffset;\n }\n /** @type {Array} */\n var markers = new Array(n);\n markers[0] = 1;\n markers[n - 1] = 1;\n /** @type {Array} */\n var stack = [offset, end - stride];\n var index = 0;\n while (stack.length > 0) {\n var last = stack.pop();\n var first = stack.pop();\n var maxSquaredDistance = 0;\n var x1 = flatCoordinates[first];\n var y1 = flatCoordinates[first + 1];\n var x2 = flatCoordinates[last];\n var y2 = flatCoordinates[last + 1];\n for (var i = first + stride; i < last; i += stride) {\n var x = flatCoordinates[i];\n var y = flatCoordinates[i + 1];\n var squaredDistance_1 = squaredSegmentDistance(x, y, x1, y1, x2, y2);\n if (squaredDistance_1 > maxSquaredDistance) {\n index = i;\n maxSquaredDistance = squaredDistance_1;\n }\n }\n if (maxSquaredDistance > squaredTolerance) {\n markers[(index - offset) / stride] = 1;\n if (first + stride < index) {\n stack.push(first, index);\n }\n if (index + stride < last) {\n stack.push(index, last);\n }\n }\n }\n for (var i = 0; i < n; ++i) {\n if (markers[i]) {\n simplifiedFlatCoordinates[simplifiedOffset++] =\n flatCoordinates[offset + i * stride];\n simplifiedFlatCoordinates[simplifiedOffset++] =\n flatCoordinates[offset + i * stride + 1];\n }\n }\n return simplifiedOffset;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @param {number} squaredTolerance Squared tolerance.\n * @param {Array} simplifiedFlatCoordinates Simplified flat\n * coordinates.\n * @param {number} simplifiedOffset Simplified offset.\n * @param {Array} simplifiedEnds Simplified ends.\n * @return {number} Simplified offset.\n */\nexport function douglasPeuckerArray(flatCoordinates, offset, ends, stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds) {\n for (var i = 0, ii = ends.length; i < ii; ++i) {\n var end = ends[i];\n simplifiedOffset = douglasPeucker(flatCoordinates, offset, end, stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset);\n simplifiedEnds.push(simplifiedOffset);\n offset = end;\n }\n return simplifiedOffset;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Endss.\n * @param {number} stride Stride.\n * @param {number} squaredTolerance Squared tolerance.\n * @param {Array} simplifiedFlatCoordinates Simplified flat\n * coordinates.\n * @param {number} simplifiedOffset Simplified offset.\n * @param {Array>} simplifiedEndss Simplified endss.\n * @return {number} Simplified offset.\n */\nexport function douglasPeuckerMultiArray(flatCoordinates, offset, endss, stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset, simplifiedEndss) {\n for (var i = 0, ii = endss.length; i < ii; ++i) {\n var ends = endss[i];\n var simplifiedEnds = [];\n simplifiedOffset = douglasPeuckerArray(flatCoordinates, offset, ends, stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds);\n simplifiedEndss.push(simplifiedEnds);\n offset = ends[ends.length - 1];\n }\n return simplifiedOffset;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {number} squaredTolerance Squared tolerance.\n * @param {Array} simplifiedFlatCoordinates Simplified flat\n * coordinates.\n * @param {number} simplifiedOffset Simplified offset.\n * @return {number} Simplified offset.\n */\nexport function radialDistance(flatCoordinates, offset, end, stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) {\n if (end <= offset + stride) {\n // zero or one point, no simplification possible, so copy and return\n for (; offset < end; offset += stride) {\n simplifiedFlatCoordinates[simplifiedOffset++] = flatCoordinates[offset];\n simplifiedFlatCoordinates[simplifiedOffset++] =\n flatCoordinates[offset + 1];\n }\n return simplifiedOffset;\n }\n var x1 = flatCoordinates[offset];\n var y1 = flatCoordinates[offset + 1];\n // copy first point\n simplifiedFlatCoordinates[simplifiedOffset++] = x1;\n simplifiedFlatCoordinates[simplifiedOffset++] = y1;\n var x2 = x1;\n var y2 = y1;\n for (offset += stride; offset < end; offset += stride) {\n x2 = flatCoordinates[offset];\n y2 = flatCoordinates[offset + 1];\n if (squaredDistance(x1, y1, x2, y2) > squaredTolerance) {\n // copy point at offset\n simplifiedFlatCoordinates[simplifiedOffset++] = x2;\n simplifiedFlatCoordinates[simplifiedOffset++] = y2;\n x1 = x2;\n y1 = y2;\n }\n }\n if (x2 != x1 || y2 != y1) {\n // copy last point\n simplifiedFlatCoordinates[simplifiedOffset++] = x2;\n simplifiedFlatCoordinates[simplifiedOffset++] = y2;\n }\n return simplifiedOffset;\n}\n/**\n * @param {number} value Value.\n * @param {number} tolerance Tolerance.\n * @return {number} Rounded value.\n */\nexport function snap(value, tolerance) {\n return tolerance * Math.round(value / tolerance);\n}\n/**\n * Simplifies a line string using an algorithm designed by Tim Schaub.\n * Coordinates are snapped to the nearest value in a virtual grid and\n * consecutive duplicate coordinates are discarded. This effectively preserves\n * topology as the simplification of any subsection of a line string is\n * independent of the rest of the line string. This means that, for examples,\n * the common edge between two polygons will be simplified to the same line\n * string independently in both polygons. This implementation uses a single\n * pass over the coordinates and eliminates intermediate collinear points.\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {number} tolerance Tolerance.\n * @param {Array} simplifiedFlatCoordinates Simplified flat\n * coordinates.\n * @param {number} simplifiedOffset Simplified offset.\n * @return {number} Simplified offset.\n */\nexport function quantize(flatCoordinates, offset, end, stride, tolerance, simplifiedFlatCoordinates, simplifiedOffset) {\n // do nothing if the line is empty\n if (offset == end) {\n return simplifiedOffset;\n }\n // snap the first coordinate (P1)\n var x1 = snap(flatCoordinates[offset], tolerance);\n var y1 = snap(flatCoordinates[offset + 1], tolerance);\n offset += stride;\n // add the first coordinate to the output\n simplifiedFlatCoordinates[simplifiedOffset++] = x1;\n simplifiedFlatCoordinates[simplifiedOffset++] = y1;\n // find the next coordinate that does not snap to the same value as the first\n // coordinate (P2)\n var x2, y2;\n do {\n x2 = snap(flatCoordinates[offset], tolerance);\n y2 = snap(flatCoordinates[offset + 1], tolerance);\n offset += stride;\n if (offset == end) {\n // all coordinates snap to the same value, the line collapses to a point\n // push the last snapped value anyway to ensure that the output contains\n // at least two points\n // FIXME should we really return at least two points anyway?\n simplifiedFlatCoordinates[simplifiedOffset++] = x2;\n simplifiedFlatCoordinates[simplifiedOffset++] = y2;\n return simplifiedOffset;\n }\n } while (x2 == x1 && y2 == y1);\n while (offset < end) {\n // snap the next coordinate (P3)\n var x3 = snap(flatCoordinates[offset], tolerance);\n var y3 = snap(flatCoordinates[offset + 1], tolerance);\n offset += stride;\n // skip P3 if it is equal to P2\n if (x3 == x2 && y3 == y2) {\n continue;\n }\n // calculate the delta between P1 and P2\n var dx1 = x2 - x1;\n var dy1 = y2 - y1;\n // calculate the delta between P3 and P1\n var dx2 = x3 - x1;\n var dy2 = y3 - y1;\n // if P1, P2, and P3 are colinear and P3 is further from P1 than P2 is from\n // P1 in the same direction then P2 is on the straight line between P1 and\n // P3\n if ((dx1 * dy2 == dy1 * dx2) &&\n ((dx1 < 0 && dx2 < dx1) || dx1 == dx2 || (dx1 > 0 && dx2 > dx1)) &&\n ((dy1 < 0 && dy2 < dy1) || dy1 == dy2 || (dy1 > 0 && dy2 > dy1))) {\n // discard P2 and set P2 = P3\n x2 = x3;\n y2 = y3;\n continue;\n }\n // either P1, P2, and P3 are not colinear, or they are colinear but P3 is\n // between P3 and P1 or on the opposite half of the line to P2. add P2,\n // and continue with P1 = P2 and P2 = P3\n simplifiedFlatCoordinates[simplifiedOffset++] = x2;\n simplifiedFlatCoordinates[simplifiedOffset++] = y2;\n x1 = x2;\n y1 = y2;\n x2 = x3;\n y2 = y3;\n }\n // add the last point (P2)\n simplifiedFlatCoordinates[simplifiedOffset++] = x2;\n simplifiedFlatCoordinates[simplifiedOffset++] = y2;\n return simplifiedOffset;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @param {number} tolerance Tolerance.\n * @param {Array} simplifiedFlatCoordinates Simplified flat\n * coordinates.\n * @param {number} simplifiedOffset Simplified offset.\n * @param {Array} simplifiedEnds Simplified ends.\n * @return {number} Simplified offset.\n */\nexport function quantizeArray(flatCoordinates, offset, ends, stride, tolerance, simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds) {\n for (var i = 0, ii = ends.length; i < ii; ++i) {\n var end = ends[i];\n simplifiedOffset = quantize(flatCoordinates, offset, end, stride, tolerance, simplifiedFlatCoordinates, simplifiedOffset);\n simplifiedEnds.push(simplifiedOffset);\n offset = end;\n }\n return simplifiedOffset;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Endss.\n * @param {number} stride Stride.\n * @param {number} tolerance Tolerance.\n * @param {Array} simplifiedFlatCoordinates Simplified flat\n * coordinates.\n * @param {number} simplifiedOffset Simplified offset.\n * @param {Array>} simplifiedEndss Simplified endss.\n * @return {number} Simplified offset.\n */\nexport function quantizeMultiArray(flatCoordinates, offset, endss, stride, tolerance, simplifiedFlatCoordinates, simplifiedOffset, simplifiedEndss) {\n for (var i = 0, ii = endss.length; i < ii; ++i) {\n var ends = endss[i];\n var simplifiedEnds = [];\n simplifiedOffset = quantizeArray(flatCoordinates, offset, ends, stride, tolerance, simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds);\n simplifiedEndss.push(simplifiedEnds);\n offset = ends[ends.length - 1];\n }\n return simplifiedOffset;\n}\n//# sourceMappingURL=simplify.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/geom/LinearRing\n */\nimport { closestSquaredDistanceXY } from '../extent.js';\nimport GeometryLayout from './GeometryLayout.js';\nimport GeometryType from './GeometryType.js';\nimport SimpleGeometry from './SimpleGeometry.js';\nimport { linearRing as linearRingArea } from './flat/area.js';\nimport { assignClosestPoint, maxSquaredDelta } from './flat/closest.js';\nimport { deflateCoordinates } from './flat/deflate.js';\nimport { inflateCoordinates } from './flat/inflate.js';\nimport { douglasPeucker } from './flat/simplify.js';\n/**\n * @classdesc\n * Linear ring geometry. Only used as part of polygon; cannot be rendered\n * on its own.\n *\n * @api\n */\nvar LinearRing = /** @class */ (function (_super) {\n __extends(LinearRing, _super);\n /**\n * @param {Array|Array} coordinates Coordinates.\n * For internal use, flat coordinates in combination with `opt_layout` are also accepted.\n * @param {GeometryLayout=} opt_layout Layout.\n */\n function LinearRing(coordinates, opt_layout) {\n var _this = _super.call(this) || this;\n /**\n * @private\n * @type {number}\n */\n _this.maxDelta_ = -1;\n /**\n * @private\n * @type {number}\n */\n _this.maxDeltaRevision_ = -1;\n if (opt_layout !== undefined && !Array.isArray(coordinates[0])) {\n _this.setFlatCoordinates(opt_layout, /** @type {Array} */ (coordinates));\n }\n else {\n _this.setCoordinates(/** @type {Array} */ (coordinates), opt_layout);\n }\n return _this;\n }\n /**\n * Make a complete copy of the geometry.\n * @return {!LinearRing} Clone.\n * @override\n * @api\n */\n LinearRing.prototype.clone = function () {\n return new LinearRing(this.flatCoordinates.slice(), this.layout);\n };\n /**\n * @inheritDoc\n */\n LinearRing.prototype.closestPointXY = function (x, y, closestPoint, minSquaredDistance) {\n if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {\n return minSquaredDistance;\n }\n if (this.maxDeltaRevision_ != this.getRevision()) {\n this.maxDelta_ = Math.sqrt(maxSquaredDelta(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0));\n this.maxDeltaRevision_ = this.getRevision();\n }\n return assignClosestPoint(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);\n };\n /**\n * Return the area of the linear ring on projected plane.\n * @return {number} Area (on projected plane).\n * @api\n */\n LinearRing.prototype.getArea = function () {\n return linearRingArea(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);\n };\n /**\n * Return the coordinates of the linear ring.\n * @return {Array} Coordinates.\n * @override\n * @api\n */\n LinearRing.prototype.getCoordinates = function () {\n return inflateCoordinates(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);\n };\n /**\n * @inheritDoc\n */\n LinearRing.prototype.getSimplifiedGeometryInternal = function (squaredTolerance) {\n var simplifiedFlatCoordinates = [];\n simplifiedFlatCoordinates.length = douglasPeucker(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, squaredTolerance, simplifiedFlatCoordinates, 0);\n return new LinearRing(simplifiedFlatCoordinates, GeometryLayout.XY);\n };\n /**\n * @inheritDoc\n * @api\n */\n LinearRing.prototype.getType = function () {\n return GeometryType.LINEAR_RING;\n };\n /**\n * @inheritDoc\n */\n LinearRing.prototype.intersectsExtent = function (extent) {\n return false;\n };\n /**\n * Set the coordinates of the linear ring.\n * @param {!Array} coordinates Coordinates.\n * @param {GeometryLayout=} opt_layout Layout.\n * @override\n * @api\n */\n LinearRing.prototype.setCoordinates = function (coordinates, opt_layout) {\n this.setLayout(opt_layout, coordinates, 1);\n if (!this.flatCoordinates) {\n this.flatCoordinates = [];\n }\n this.flatCoordinates.length = deflateCoordinates(this.flatCoordinates, 0, coordinates, this.stride);\n this.changed();\n };\n return LinearRing;\n}(SimpleGeometry));\nexport default LinearRing;\n//# sourceMappingURL=LinearRing.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/geom/Point\n */\nimport { createOrUpdateFromCoordinate, containsXY } from '../extent.js';\nimport GeometryType from './GeometryType.js';\nimport SimpleGeometry from './SimpleGeometry.js';\nimport { deflateCoordinate } from './flat/deflate.js';\nimport { squaredDistance as squaredDx } from '../math.js';\n/**\n * @classdesc\n * Point geometry.\n *\n * @api\n */\nvar Point = /** @class */ (function (_super) {\n __extends(Point, _super);\n /**\n * @param {import(\"../coordinate.js\").Coordinate} coordinates Coordinates.\n * @param {import(\"./GeometryLayout.js\").default=} opt_layout Layout.\n */\n function Point(coordinates, opt_layout) {\n var _this = _super.call(this) || this;\n _this.setCoordinates(coordinates, opt_layout);\n return _this;\n }\n /**\n * Make a complete copy of the geometry.\n * @return {!Point} Clone.\n * @override\n * @api\n */\n Point.prototype.clone = function () {\n var point = new Point(this.flatCoordinates.slice(), this.layout);\n return point;\n };\n /**\n * @inheritDoc\n */\n Point.prototype.closestPointXY = function (x, y, closestPoint, minSquaredDistance) {\n var flatCoordinates = this.flatCoordinates;\n var squaredDistance = squaredDx(x, y, flatCoordinates[0], flatCoordinates[1]);\n if (squaredDistance < minSquaredDistance) {\n var stride = this.stride;\n for (var i = 0; i < stride; ++i) {\n closestPoint[i] = flatCoordinates[i];\n }\n closestPoint.length = stride;\n return squaredDistance;\n }\n else {\n return minSquaredDistance;\n }\n };\n /**\n * Return the coordinate of the point.\n * @return {import(\"../coordinate.js\").Coordinate} Coordinates.\n * @override\n * @api\n */\n Point.prototype.getCoordinates = function () {\n return !this.flatCoordinates ? [] : this.flatCoordinates.slice();\n };\n /**\n * @inheritDoc\n */\n Point.prototype.computeExtent = function (extent) {\n return createOrUpdateFromCoordinate(this.flatCoordinates, extent);\n };\n /**\n * @inheritDoc\n * @api\n */\n Point.prototype.getType = function () {\n return GeometryType.POINT;\n };\n /**\n * @inheritDoc\n * @api\n */\n Point.prototype.intersectsExtent = function (extent) {\n return containsXY(extent, this.flatCoordinates[0], this.flatCoordinates[1]);\n };\n /**\n * @inheritDoc\n * @api\n */\n Point.prototype.setCoordinates = function (coordinates, opt_layout) {\n this.setLayout(opt_layout, coordinates, 0);\n if (!this.flatCoordinates) {\n this.flatCoordinates = [];\n }\n this.flatCoordinates.length = deflateCoordinate(this.flatCoordinates, 0, coordinates, this.stride);\n this.changed();\n };\n return Point;\n}(SimpleGeometry));\nexport default Point;\n//# sourceMappingURL=Point.js.map","/**\n * @module ol/geom/flat/contains\n */\nimport { forEachCorner } from '../../extent.js';\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {import(\"../../extent.js\").Extent} extent Extent.\n * @return {boolean} Contains extent.\n */\nexport function linearRingContainsExtent(flatCoordinates, offset, end, stride, extent) {\n var outside = forEachCorner(extent, \n /**\n * @param {import(\"../../coordinate.js\").Coordinate} coordinate Coordinate.\n * @return {boolean} Contains (x, y).\n */\n function (coordinate) {\n return !linearRingContainsXY(flatCoordinates, offset, end, stride, coordinate[0], coordinate[1]);\n });\n return !outside;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {number} x X.\n * @param {number} y Y.\n * @return {boolean} Contains (x, y).\n */\nexport function linearRingContainsXY(flatCoordinates, offset, end, stride, x, y) {\n // http://geomalgorithms.com/a03-_inclusion.html\n // Copyright 2000 softSurfer, 2012 Dan Sunday\n // This code may be freely used and modified for any purpose\n // providing that this copyright notice is included with it.\n // SoftSurfer makes no warranty for this code, and cannot be held\n // liable for any real or imagined damage resulting from its use.\n // Users of this code must verify correctness for their application.\n var wn = 0;\n var x1 = flatCoordinates[end - stride];\n var y1 = flatCoordinates[end - stride + 1];\n for (; offset < end; offset += stride) {\n var x2 = flatCoordinates[offset];\n var y2 = flatCoordinates[offset + 1];\n if (y1 <= y) {\n if (y2 > y && ((x2 - x1) * (y - y1)) - ((x - x1) * (y2 - y1)) > 0) {\n wn++;\n }\n }\n else if (y2 <= y && ((x2 - x1) * (y - y1)) - ((x - x1) * (y2 - y1)) < 0) {\n wn--;\n }\n x1 = x2;\n y1 = y2;\n }\n return wn !== 0;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @param {number} x X.\n * @param {number} y Y.\n * @return {boolean} Contains (x, y).\n */\nexport function linearRingsContainsXY(flatCoordinates, offset, ends, stride, x, y) {\n if (ends.length === 0) {\n return false;\n }\n if (!linearRingContainsXY(flatCoordinates, offset, ends[0], stride, x, y)) {\n return false;\n }\n for (var i = 1, ii = ends.length; i < ii; ++i) {\n if (linearRingContainsXY(flatCoordinates, ends[i - 1], ends[i], stride, x, y)) {\n return false;\n }\n }\n return true;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Endss.\n * @param {number} stride Stride.\n * @param {number} x X.\n * @param {number} y Y.\n * @return {boolean} Contains (x, y).\n */\nexport function linearRingssContainsXY(flatCoordinates, offset, endss, stride, x, y) {\n if (endss.length === 0) {\n return false;\n }\n for (var i = 0, ii = endss.length; i < ii; ++i) {\n var ends = endss[i];\n if (linearRingsContainsXY(flatCoordinates, offset, ends, stride, x, y)) {\n return true;\n }\n offset = ends[ends.length - 1];\n }\n return false;\n}\n//# sourceMappingURL=contains.js.map","/**\n * @module ol/geom/flat/interiorpoint\n */\nimport { numberSafeCompareFunction } from '../../array.js';\nimport { linearRingsContainsXY } from './contains.js';\n/**\n * Calculates a point that is likely to lie in the interior of the linear rings.\n * Inspired by JTS's com.vividsolutions.jts.geom.Geometry#getInteriorPoint.\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @param {Array} flatCenters Flat centers.\n * @param {number} flatCentersOffset Flat center offset.\n * @param {Array=} opt_dest Destination.\n * @return {Array} Destination point as XYM coordinate, where M is the\n * length of the horizontal intersection that the point belongs to.\n */\nexport function getInteriorPointOfArray(flatCoordinates, offset, ends, stride, flatCenters, flatCentersOffset, opt_dest) {\n var i, ii, x, x1, x2, y1, y2;\n var y = flatCenters[flatCentersOffset + 1];\n /** @type {Array} */\n var intersections = [];\n // Calculate intersections with the horizontal line\n for (var r = 0, rr = ends.length; r < rr; ++r) {\n var end = ends[r];\n x1 = flatCoordinates[end - stride];\n y1 = flatCoordinates[end - stride + 1];\n for (i = offset; i < end; i += stride) {\n x2 = flatCoordinates[i];\n y2 = flatCoordinates[i + 1];\n if ((y <= y1 && y2 <= y) || (y1 <= y && y <= y2)) {\n x = (y - y1) / (y2 - y1) * (x2 - x1) + x1;\n intersections.push(x);\n }\n x1 = x2;\n y1 = y2;\n }\n }\n // Find the longest segment of the horizontal line that has its center point\n // inside the linear ring.\n var pointX = NaN;\n var maxSegmentLength = -Infinity;\n intersections.sort(numberSafeCompareFunction);\n x1 = intersections[0];\n for (i = 1, ii = intersections.length; i < ii; ++i) {\n x2 = intersections[i];\n var segmentLength = Math.abs(x2 - x1);\n if (segmentLength > maxSegmentLength) {\n x = (x1 + x2) / 2;\n if (linearRingsContainsXY(flatCoordinates, offset, ends, stride, x, y)) {\n pointX = x;\n maxSegmentLength = segmentLength;\n }\n }\n x1 = x2;\n }\n if (isNaN(pointX)) {\n // There is no horizontal line that has its center point inside the linear\n // ring. Use the center of the the linear ring's extent.\n pointX = flatCenters[flatCentersOffset];\n }\n if (opt_dest) {\n opt_dest.push(pointX, y, maxSegmentLength);\n return opt_dest;\n }\n else {\n return [pointX, y, maxSegmentLength];\n }\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Endss.\n * @param {number} stride Stride.\n * @param {Array} flatCenters Flat centers.\n * @return {Array} Interior points as XYM coordinates, where M is the\n * length of the horizontal intersection that the point belongs to.\n */\nexport function getInteriorPointsOfMultiArray(flatCoordinates, offset, endss, stride, flatCenters) {\n var interiorPoints = [];\n for (var i = 0, ii = endss.length; i < ii; ++i) {\n var ends = endss[i];\n interiorPoints = getInteriorPointOfArray(flatCoordinates, offset, ends, stride, flatCenters, 2 * i, interiorPoints);\n offset = ends[ends.length - 1];\n }\n return interiorPoints;\n}\n//# sourceMappingURL=interiorpoint.js.map","/**\n * @module ol/geom/flat/segments\n */\n/**\n * This function calls `callback` for each segment of the flat coordinates\n * array. If the callback returns a truthy value the function returns that\n * value immediately. Otherwise the function returns `false`.\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {function(import(\"../../coordinate.js\").Coordinate, import(\"../../coordinate.js\").Coordinate): T} callback Function\n * called for each segment.\n * @return {T|boolean} Value.\n * @template T\n */\nexport function forEach(flatCoordinates, offset, end, stride, callback) {\n var point1 = [flatCoordinates[offset], flatCoordinates[offset + 1]];\n var point2 = [];\n var ret;\n for (; (offset + stride) < end; offset += stride) {\n point2[0] = flatCoordinates[offset + stride];\n point2[1] = flatCoordinates[offset + stride + 1];\n ret = callback(point1, point2);\n if (ret) {\n return ret;\n }\n point1[0] = point2[0];\n point1[1] = point2[1];\n }\n return false;\n}\n//# sourceMappingURL=segments.js.map","/**\n * @module ol/geom/flat/intersectsextent\n */\nimport { containsExtent, createEmpty, extendFlatCoordinates, intersects, intersectsSegment } from '../../extent.js';\nimport { linearRingContainsXY, linearRingContainsExtent } from './contains.js';\nimport { forEach as forEachSegment } from './segments.js';\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {import(\"../../extent.js\").Extent} extent Extent.\n * @return {boolean} True if the geometry and the extent intersect.\n */\nexport function intersectsLineString(flatCoordinates, offset, end, stride, extent) {\n var coordinatesExtent = extendFlatCoordinates(createEmpty(), flatCoordinates, offset, end, stride);\n if (!intersects(extent, coordinatesExtent)) {\n return false;\n }\n if (containsExtent(extent, coordinatesExtent)) {\n return true;\n }\n if (coordinatesExtent[0] >= extent[0] &&\n coordinatesExtent[2] <= extent[2]) {\n return true;\n }\n if (coordinatesExtent[1] >= extent[1] &&\n coordinatesExtent[3] <= extent[3]) {\n return true;\n }\n return forEachSegment(flatCoordinates, offset, end, stride, \n /**\n * @param {import(\"../../coordinate.js\").Coordinate} point1 Start point.\n * @param {import(\"../../coordinate.js\").Coordinate} point2 End point.\n * @return {boolean} `true` if the segment and the extent intersect,\n * `false` otherwise.\n */\n function (point1, point2) {\n return intersectsSegment(extent, point1, point2);\n });\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @param {import(\"../../extent.js\").Extent} extent Extent.\n * @return {boolean} True if the geometry and the extent intersect.\n */\nexport function intersectsLineStringArray(flatCoordinates, offset, ends, stride, extent) {\n for (var i = 0, ii = ends.length; i < ii; ++i) {\n if (intersectsLineString(flatCoordinates, offset, ends[i], stride, extent)) {\n return true;\n }\n offset = ends[i];\n }\n return false;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @param {import(\"../../extent.js\").Extent} extent Extent.\n * @return {boolean} True if the geometry and the extent intersect.\n */\nexport function intersectsLinearRing(flatCoordinates, offset, end, stride, extent) {\n if (intersectsLineString(flatCoordinates, offset, end, stride, extent)) {\n return true;\n }\n if (linearRingContainsXY(flatCoordinates, offset, end, stride, extent[0], extent[1])) {\n return true;\n }\n if (linearRingContainsXY(flatCoordinates, offset, end, stride, extent[0], extent[3])) {\n return true;\n }\n if (linearRingContainsXY(flatCoordinates, offset, end, stride, extent[2], extent[1])) {\n return true;\n }\n if (linearRingContainsXY(flatCoordinates, offset, end, stride, extent[2], extent[3])) {\n return true;\n }\n return false;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @param {import(\"../../extent.js\").Extent} extent Extent.\n * @return {boolean} True if the geometry and the extent intersect.\n */\nexport function intersectsLinearRingArray(flatCoordinates, offset, ends, stride, extent) {\n if (!intersectsLinearRing(flatCoordinates, offset, ends[0], stride, extent)) {\n return false;\n }\n if (ends.length === 1) {\n return true;\n }\n for (var i = 1, ii = ends.length; i < ii; ++i) {\n if (linearRingContainsExtent(flatCoordinates, ends[i - 1], ends[i], stride, extent)) {\n if (!intersectsLineString(flatCoordinates, ends[i - 1], ends[i], stride, extent)) {\n return false;\n }\n }\n }\n return true;\n}\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Endss.\n * @param {number} stride Stride.\n * @param {import(\"../../extent.js\").Extent} extent Extent.\n * @return {boolean} True if the geometry and the extent intersect.\n */\nexport function intersectsLinearRingMultiArray(flatCoordinates, offset, endss, stride, extent) {\n for (var i = 0, ii = endss.length; i < ii; ++i) {\n var ends = endss[i];\n if (intersectsLinearRingArray(flatCoordinates, offset, ends, stride, extent)) {\n return true;\n }\n offset = ends[ends.length - 1];\n }\n return false;\n}\n//# sourceMappingURL=intersectsextent.js.map","/**\n * @module ol/geom/flat/reverse\n */\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n */\nexport function coordinates(flatCoordinates, offset, end, stride) {\n while (offset < end - stride) {\n for (var i = 0; i < stride; ++i) {\n var tmp = flatCoordinates[offset + i];\n flatCoordinates[offset + i] = flatCoordinates[end - stride + i];\n flatCoordinates[end - stride + i] = tmp;\n }\n offset += stride;\n end -= stride;\n }\n}\n//# sourceMappingURL=reverse.js.map","/**\n * @module ol/geom/flat/orient\n */\nimport { coordinates as reverseCoordinates } from './reverse.js';\n/**\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {number} end End.\n * @param {number} stride Stride.\n * @return {boolean} Is clockwise.\n */\nexport function linearRingIsClockwise(flatCoordinates, offset, end, stride) {\n // http://tinyurl.com/clockwise-method\n // https://github.com/OSGeo/gdal/blob/trunk/gdal/ogr/ogrlinearring.cpp\n var edge = 0;\n var x1 = flatCoordinates[end - stride];\n var y1 = flatCoordinates[end - stride + 1];\n for (; offset < end; offset += stride) {\n var x2 = flatCoordinates[offset];\n var y2 = flatCoordinates[offset + 1];\n edge += (x2 - x1) * (y2 + y1);\n x1 = x2;\n y1 = y2;\n }\n return edge > 0;\n}\n/**\n * Determines if linear rings are oriented. By default, left-hand orientation\n * is tested (first ring must be clockwise, remaining rings counter-clockwise).\n * To test for right-hand orientation, use the `opt_right` argument.\n *\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Array of end indexes.\n * @param {number} stride Stride.\n * @param {boolean=} opt_right Test for right-hand orientation\n * (counter-clockwise exterior ring and clockwise interior rings).\n * @return {boolean} Rings are correctly oriented.\n */\nexport function linearRingsAreOriented(flatCoordinates, offset, ends, stride, opt_right) {\n var right = opt_right !== undefined ? opt_right : false;\n for (var i = 0, ii = ends.length; i < ii; ++i) {\n var end = ends[i];\n var isClockwise = linearRingIsClockwise(flatCoordinates, offset, end, stride);\n if (i === 0) {\n if ((right && isClockwise) || (!right && !isClockwise)) {\n return false;\n }\n }\n else {\n if ((right && !isClockwise) || (!right && isClockwise)) {\n return false;\n }\n }\n offset = end;\n }\n return true;\n}\n/**\n * Determines if linear rings are oriented. By default, left-hand orientation\n * is tested (first ring must be clockwise, remaining rings counter-clockwise).\n * To test for right-hand orientation, use the `opt_right` argument.\n *\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Array of array of end indexes.\n * @param {number} stride Stride.\n * @param {boolean=} opt_right Test for right-hand orientation\n * (counter-clockwise exterior ring and clockwise interior rings).\n * @return {boolean} Rings are correctly oriented.\n */\nexport function linearRingssAreOriented(flatCoordinates, offset, endss, stride, opt_right) {\n for (var i = 0, ii = endss.length; i < ii; ++i) {\n var ends = endss[i];\n if (!linearRingsAreOriented(flatCoordinates, offset, ends, stride, opt_right)) {\n return false;\n }\n if (ends.length) {\n offset = ends[ends.length - 1];\n }\n }\n return true;\n}\n/**\n * Orient coordinates in a flat array of linear rings. By default, rings\n * are oriented following the left-hand rule (clockwise for exterior and\n * counter-clockwise for interior rings). To orient according to the\n * right-hand rule, use the `opt_right` argument.\n *\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array} ends Ends.\n * @param {number} stride Stride.\n * @param {boolean=} opt_right Follow the right-hand rule for orientation.\n * @return {number} End.\n */\nexport function orientLinearRings(flatCoordinates, offset, ends, stride, opt_right) {\n var right = opt_right !== undefined ? opt_right : false;\n for (var i = 0, ii = ends.length; i < ii; ++i) {\n var end = ends[i];\n var isClockwise = linearRingIsClockwise(flatCoordinates, offset, end, stride);\n var reverse = i === 0 ?\n (right && isClockwise) || (!right && !isClockwise) :\n (right && !isClockwise) || (!right && isClockwise);\n if (reverse) {\n reverseCoordinates(flatCoordinates, offset, end, stride);\n }\n offset = end;\n }\n return offset;\n}\n/**\n * Orient coordinates in a flat array of linear rings. By default, rings\n * are oriented following the left-hand rule (clockwise for exterior and\n * counter-clockwise for interior rings). To orient according to the\n * right-hand rule, use the `opt_right` argument.\n *\n * @param {Array} flatCoordinates Flat coordinates.\n * @param {number} offset Offset.\n * @param {Array>} endss Array of array of end indexes.\n * @param {number} stride Stride.\n * @param {boolean=} opt_right Follow the right-hand rule for orientation.\n * @return {number} End.\n */\nexport function orientLinearRingsArray(flatCoordinates, offset, endss, stride, opt_right) {\n for (var i = 0, ii = endss.length; i < ii; ++i) {\n offset = orientLinearRings(flatCoordinates, offset, endss[i], stride, opt_right);\n }\n return offset;\n}\n//# sourceMappingURL=orient.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/geom/Polygon\n */\nimport { extend } from '../array.js';\nimport { closestSquaredDistanceXY, getCenter } from '../extent.js';\nimport GeometryLayout from './GeometryLayout.js';\nimport GeometryType from './GeometryType.js';\nimport LinearRing from './LinearRing.js';\nimport Point from './Point.js';\nimport SimpleGeometry from './SimpleGeometry.js';\nimport { offset as sphereOffset } from '../sphere.js';\nimport { linearRings as linearRingsArea } from './flat/area.js';\nimport { assignClosestArrayPoint, arrayMaxSquaredDelta } from './flat/closest.js';\nimport { linearRingsContainsXY } from './flat/contains.js';\nimport { deflateCoordinatesArray } from './flat/deflate.js';\nimport { inflateCoordinatesArray } from './flat/inflate.js';\nimport { getInteriorPointOfArray } from './flat/interiorpoint.js';\nimport { intersectsLinearRingArray } from './flat/intersectsextent.js';\nimport { linearRingsAreOriented, orientLinearRings } from './flat/orient.js';\nimport { quantizeArray } from './flat/simplify.js';\nimport { modulo } from '../math.js';\n/**\n * @classdesc\n * Polygon geometry.\n *\n * @api\n */\nvar Polygon = /** @class */ (function (_super) {\n __extends(Polygon, _super);\n /**\n * @param {!Array>|!Array} coordinates\n * Array of linear rings that define the polygon. The first linear ring of the\n * array defines the outer-boundary or surface of the polygon. Each subsequent\n * linear ring defines a hole in the surface of the polygon. A linear ring is\n * an array of vertices' coordinates where the first coordinate and the last are\n * equivalent. (For internal use, flat coordinates in combination with\n * `opt_layout` and `opt_ends` are also accepted.)\n * @param {GeometryLayout=} opt_layout Layout.\n * @param {Array=} opt_ends Ends (for internal use with flat coordinates).\n */\n function Polygon(coordinates, opt_layout, opt_ends) {\n var _this = _super.call(this) || this;\n /**\n * @type {Array}\n * @private\n */\n _this.ends_ = [];\n /**\n * @private\n * @type {number}\n */\n _this.flatInteriorPointRevision_ = -1;\n /**\n * @private\n * @type {import(\"../coordinate.js\").Coordinate}\n */\n _this.flatInteriorPoint_ = null;\n /**\n * @private\n * @type {number}\n */\n _this.maxDelta_ = -1;\n /**\n * @private\n * @type {number}\n */\n _this.maxDeltaRevision_ = -1;\n /**\n * @private\n * @type {number}\n */\n _this.orientedRevision_ = -1;\n /**\n * @private\n * @type {Array}\n */\n _this.orientedFlatCoordinates_ = null;\n if (opt_layout !== undefined && opt_ends) {\n _this.setFlatCoordinates(opt_layout, /** @type {Array} */ (coordinates));\n _this.ends_ = opt_ends;\n }\n else {\n _this.setCoordinates(/** @type {Array>} */ (coordinates), opt_layout);\n }\n return _this;\n }\n /**\n * Append the passed linear ring to this polygon.\n * @param {LinearRing} linearRing Linear ring.\n * @api\n */\n Polygon.prototype.appendLinearRing = function (linearRing) {\n if (!this.flatCoordinates) {\n this.flatCoordinates = linearRing.getFlatCoordinates().slice();\n }\n else {\n extend(this.flatCoordinates, linearRing.getFlatCoordinates());\n }\n this.ends_.push(this.flatCoordinates.length);\n this.changed();\n };\n /**\n * Make a complete copy of the geometry.\n * @return {!Polygon} Clone.\n * @override\n * @api\n */\n Polygon.prototype.clone = function () {\n return new Polygon(this.flatCoordinates.slice(), this.layout, this.ends_.slice());\n };\n /**\n * @inheritDoc\n */\n Polygon.prototype.closestPointXY = function (x, y, closestPoint, minSquaredDistance) {\n if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {\n return minSquaredDistance;\n }\n if (this.maxDeltaRevision_ != this.getRevision()) {\n this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta(this.flatCoordinates, 0, this.ends_, this.stride, 0));\n this.maxDeltaRevision_ = this.getRevision();\n }\n return assignClosestArrayPoint(this.flatCoordinates, 0, this.ends_, this.stride, this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);\n };\n /**\n * @inheritDoc\n */\n Polygon.prototype.containsXY = function (x, y) {\n return linearRingsContainsXY(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, x, y);\n };\n /**\n * Return the area of the polygon on projected plane.\n * @return {number} Area (on projected plane).\n * @api\n */\n Polygon.prototype.getArea = function () {\n return linearRingsArea(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride);\n };\n /**\n * Get the coordinate array for this geometry. This array has the structure\n * of a GeoJSON coordinate array for polygons.\n *\n * @param {boolean=} opt_right Orient coordinates according to the right-hand\n * rule (counter-clockwise for exterior and clockwise for interior rings).\n * If `false`, coordinates will be oriented according to the left-hand rule\n * (clockwise for exterior and counter-clockwise for interior rings).\n * By default, coordinate orientation will depend on how the geometry was\n * constructed.\n * @return {Array>} Coordinates.\n * @override\n * @api\n */\n Polygon.prototype.getCoordinates = function (opt_right) {\n var flatCoordinates;\n if (opt_right !== undefined) {\n flatCoordinates = this.getOrientedFlatCoordinates().slice();\n orientLinearRings(flatCoordinates, 0, this.ends_, this.stride, opt_right);\n }\n else {\n flatCoordinates = this.flatCoordinates;\n }\n return inflateCoordinatesArray(flatCoordinates, 0, this.ends_, this.stride);\n };\n /**\n * @return {Array} Ends.\n */\n Polygon.prototype.getEnds = function () {\n return this.ends_;\n };\n /**\n * @return {Array} Interior point.\n */\n Polygon.prototype.getFlatInteriorPoint = function () {\n if (this.flatInteriorPointRevision_ != this.getRevision()) {\n var flatCenter = getCenter(this.getExtent());\n this.flatInteriorPoint_ = getInteriorPointOfArray(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, flatCenter, 0);\n this.flatInteriorPointRevision_ = this.getRevision();\n }\n return this.flatInteriorPoint_;\n };\n /**\n * Return an interior point of the polygon.\n * @return {Point} Interior point as XYM coordinate, where M is the\n * length of the horizontal intersection that the point belongs to.\n * @api\n */\n Polygon.prototype.getInteriorPoint = function () {\n return new Point(this.getFlatInteriorPoint(), GeometryLayout.XYM);\n };\n /**\n * Return the number of rings of the polygon, this includes the exterior\n * ring and any interior rings.\n *\n * @return {number} Number of rings.\n * @api\n */\n Polygon.prototype.getLinearRingCount = function () {\n return this.ends_.length;\n };\n /**\n * Return the Nth linear ring of the polygon geometry. Return `null` if the\n * given index is out of range.\n * The exterior linear ring is available at index `0` and the interior rings\n * at index `1` and beyond.\n *\n * @param {number} index Index.\n * @return {LinearRing} Linear ring.\n * @api\n */\n Polygon.prototype.getLinearRing = function (index) {\n if (index < 0 || this.ends_.length <= index) {\n return null;\n }\n return new LinearRing(this.flatCoordinates.slice(index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]), this.layout);\n };\n /**\n * Return the linear rings of the polygon.\n * @return {Array} Linear rings.\n * @api\n */\n Polygon.prototype.getLinearRings = function () {\n var layout = this.layout;\n var flatCoordinates = this.flatCoordinates;\n var ends = this.ends_;\n var linearRings = [];\n var offset = 0;\n for (var i = 0, ii = ends.length; i < ii; ++i) {\n var end = ends[i];\n var linearRing = new LinearRing(flatCoordinates.slice(offset, end), layout);\n linearRings.push(linearRing);\n offset = end;\n }\n return linearRings;\n };\n /**\n * @return {Array} Oriented flat coordinates.\n */\n Polygon.prototype.getOrientedFlatCoordinates = function () {\n if (this.orientedRevision_ != this.getRevision()) {\n var flatCoordinates = this.flatCoordinates;\n if (linearRingsAreOriented(flatCoordinates, 0, this.ends_, this.stride)) {\n this.orientedFlatCoordinates_ = flatCoordinates;\n }\n else {\n this.orientedFlatCoordinates_ = flatCoordinates.slice();\n this.orientedFlatCoordinates_.length =\n orientLinearRings(this.orientedFlatCoordinates_, 0, this.ends_, this.stride);\n }\n this.orientedRevision_ = this.getRevision();\n }\n return this.orientedFlatCoordinates_;\n };\n /**\n * @inheritDoc\n */\n Polygon.prototype.getSimplifiedGeometryInternal = function (squaredTolerance) {\n var simplifiedFlatCoordinates = [];\n var simplifiedEnds = [];\n simplifiedFlatCoordinates.length = quantizeArray(this.flatCoordinates, 0, this.ends_, this.stride, Math.sqrt(squaredTolerance), simplifiedFlatCoordinates, 0, simplifiedEnds);\n return new Polygon(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEnds);\n };\n /**\n * @inheritDoc\n * @api\n */\n Polygon.prototype.getType = function () {\n return GeometryType.POLYGON;\n };\n /**\n * @inheritDoc\n * @api\n */\n Polygon.prototype.intersectsExtent = function (extent) {\n return intersectsLinearRingArray(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent);\n };\n /**\n * Set the coordinates of the polygon.\n * @param {!Array>} coordinates Coordinates.\n * @param {GeometryLayout=} opt_layout Layout.\n * @override\n * @api\n */\n Polygon.prototype.setCoordinates = function (coordinates, opt_layout) {\n this.setLayout(opt_layout, coordinates, 2);\n if (!this.flatCoordinates) {\n this.flatCoordinates = [];\n }\n var ends = deflateCoordinatesArray(this.flatCoordinates, 0, coordinates, this.stride, this.ends_);\n this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];\n this.changed();\n };\n return Polygon;\n}(SimpleGeometry));\nexport default Polygon;\n/**\n * Create an approximation of a circle on the surface of a sphere.\n * @param {import(\"../coordinate.js\").Coordinate} center Center (`[lon, lat]` in degrees).\n * @param {number} radius The great-circle distance from the center to\n * the polygon vertices.\n * @param {number=} opt_n Optional number of vertices for the resulting\n * polygon. Default is `32`.\n * @param {number=} opt_sphereRadius Optional radius for the sphere (defaults to\n * the Earth's mean radius using the WGS84 ellipsoid).\n * @return {Polygon} The \"circular\" polygon.\n * @api\n */\nexport function circular(center, radius, opt_n, opt_sphereRadius) {\n var n = opt_n ? opt_n : 32;\n /** @type {Array} */\n var flatCoordinates = [];\n for (var i = 0; i < n; ++i) {\n extend(flatCoordinates, sphereOffset(center, radius, 2 * Math.PI * i / n, opt_sphereRadius));\n }\n flatCoordinates.push(flatCoordinates[0], flatCoordinates[1]);\n return new Polygon(flatCoordinates, GeometryLayout.XY, [flatCoordinates.length]);\n}\n/**\n * Create a polygon from an extent. The layout used is `XY`.\n * @param {import(\"../extent.js\").Extent} extent The extent.\n * @return {Polygon} The polygon.\n * @api\n */\nexport function fromExtent(extent) {\n var minX = extent[0];\n var minY = extent[1];\n var maxX = extent[2];\n var maxY = extent[3];\n var flatCoordinates = [minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY];\n return new Polygon(flatCoordinates, GeometryLayout.XY, [flatCoordinates.length]);\n}\n/**\n * Create a regular polygon from a circle.\n * @param {import(\"./Circle.js\").default} circle Circle geometry.\n * @param {number=} opt_sides Number of sides of the polygon. Default is 32.\n * @param {number=} opt_angle Start angle for the first vertex of the polygon in\n * radians. Default is 0.\n * @return {Polygon} Polygon geometry.\n * @api\n */\nexport function fromCircle(circle, opt_sides, opt_angle) {\n var sides = opt_sides ? opt_sides : 32;\n var stride = circle.getStride();\n var layout = circle.getLayout();\n var center = circle.getCenter();\n var arrayLength = stride * (sides + 1);\n var flatCoordinates = new Array(arrayLength);\n for (var i = 0; i < arrayLength; i += stride) {\n flatCoordinates[i] = 0;\n flatCoordinates[i + 1] = 0;\n for (var j = 2; j < stride; j++) {\n flatCoordinates[i + j] = center[j];\n }\n }\n var ends = [flatCoordinates.length];\n var polygon = new Polygon(flatCoordinates, layout, ends);\n makeRegular(polygon, center, circle.getRadius(), opt_angle);\n return polygon;\n}\n/**\n * Modify the coordinates of a polygon to make it a regular polygon.\n * @param {Polygon} polygon Polygon geometry.\n * @param {import(\"../coordinate.js\").Coordinate} center Center of the regular polygon.\n * @param {number} radius Radius of the regular polygon.\n * @param {number=} opt_angle Start angle for the first vertex of the polygon in\n * radians. Default is 0.\n */\nexport function makeRegular(polygon, center, radius, opt_angle) {\n var flatCoordinates = polygon.getFlatCoordinates();\n var stride = polygon.getStride();\n var sides = flatCoordinates.length / stride - 1;\n var startAngle = opt_angle ? opt_angle : 0;\n for (var i = 0; i <= sides; ++i) {\n var offset = i * stride;\n var angle = startAngle + (modulo(i, sides) * 2 * Math.PI / sides);\n flatCoordinates[offset] = center[0] + (radius * Math.cos(angle));\n flatCoordinates[offset + 1] = center[1] + (radius * Math.sin(angle));\n }\n polygon.changed();\n}\n//# sourceMappingURL=Polygon.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/Geolocation\n */\nimport BaseObject, { getChangeEventType } from './Object.js';\nimport BaseEvent from './events/Event.js';\nimport EventType from './events/EventType.js';\nimport { circular as circularPolygon } from './geom/Polygon.js';\nimport { toRadians } from './math.js';\nimport { get as getProjection, getTransformFromProjections, identityTransform } from './proj.js';\n/**\n * @enum {string}\n */\nvar Property = {\n ACCURACY: 'accuracy',\n ACCURACY_GEOMETRY: 'accuracyGeometry',\n ALTITUDE: 'altitude',\n ALTITUDE_ACCURACY: 'altitudeAccuracy',\n HEADING: 'heading',\n POSITION: 'position',\n PROJECTION: 'projection',\n SPEED: 'speed',\n TRACKING: 'tracking',\n TRACKING_OPTIONS: 'trackingOptions'\n};\n/**\n * @classdesc\n * Events emitted on Geolocation error.\n */\nvar GeolocationError = /** @class */ (function (_super) {\n __extends(GeolocationError, _super);\n /**\n * @param {PositionError} error error object.\n */\n function GeolocationError(error) {\n var _this = _super.call(this, EventType.ERROR) || this;\n /**\n * @type {number}\n */\n _this.code = error.code;\n /**\n * @type {string}\n */\n _this.message = error.message;\n return _this;\n }\n return GeolocationError;\n}(BaseEvent));\n/**\n * @typedef {Object} Options\n * @property {boolean} [tracking=false] Start Tracking right after\n * instantiation.\n * @property {PositionOptions} [trackingOptions] Tracking options.\n * See http://www.w3.org/TR/geolocation-API/#position_options_interface.\n * @property {import(\"./proj.js\").ProjectionLike} [projection] The projection the position\n * is reported in.\n */\n/**\n * @classdesc\n * Helper class for providing HTML5 Geolocation capabilities.\n * The [Geolocation API](http://www.w3.org/TR/geolocation-API/)\n * is used to locate a user's position.\n *\n * To get notified of position changes, register a listener for the generic\n * `change` event on your instance of {@link module:ol/Geolocation~Geolocation}.\n *\n * Example:\n *\n * var geolocation = new Geolocation({\n * // take the projection to use from the map's view\n * projection: view.getProjection()\n * });\n * // listen to changes in position\n * geolocation.on('change', function(evt) {\n * window.console.log(geolocation.getPosition());\n * });\n *\n * @fires module:ol/events/Event~BaseEvent#event:error\n * @api\n */\nvar Geolocation = /** @class */ (function (_super) {\n __extends(Geolocation, _super);\n /**\n * @param {Options=} opt_options Options.\n */\n function Geolocation(opt_options) {\n var _this = _super.call(this) || this;\n var options = opt_options || {};\n /**\n * The unprojected (EPSG:4326) device position.\n * @private\n * @type {?import(\"./coordinate.js\").Coordinate}\n */\n _this.position_ = null;\n /**\n * @private\n * @type {import(\"./proj.js\").TransformFunction}\n */\n _this.transform_ = identityTransform;\n /**\n * @private\n * @type {number|undefined}\n */\n _this.watchId_ = undefined;\n _this.addEventListener(getChangeEventType(Property.PROJECTION), _this.handleProjectionChanged_);\n _this.addEventListener(getChangeEventType(Property.TRACKING), _this.handleTrackingChanged_);\n if (options.projection !== undefined) {\n _this.setProjection(options.projection);\n }\n if (options.trackingOptions !== undefined) {\n _this.setTrackingOptions(options.trackingOptions);\n }\n _this.setTracking(options.tracking !== undefined ? options.tracking : false);\n return _this;\n }\n /**\n * @inheritDoc\n */\n Geolocation.prototype.disposeInternal = function () {\n this.setTracking(false);\n _super.prototype.disposeInternal.call(this);\n };\n /**\n * @private\n */\n Geolocation.prototype.handleProjectionChanged_ = function () {\n var projection = this.getProjection();\n if (projection) {\n this.transform_ = getTransformFromProjections(getProjection('EPSG:4326'), projection);\n if (this.position_) {\n this.set(Property.POSITION, this.transform_(this.position_));\n }\n }\n };\n /**\n * @private\n */\n Geolocation.prototype.handleTrackingChanged_ = function () {\n if ('geolocation' in navigator) {\n var tracking = this.getTracking();\n if (tracking && this.watchId_ === undefined) {\n this.watchId_ = navigator.geolocation.watchPosition(this.positionChange_.bind(this), this.positionError_.bind(this), this.getTrackingOptions());\n }\n else if (!tracking && this.watchId_ !== undefined) {\n navigator.geolocation.clearWatch(this.watchId_);\n this.watchId_ = undefined;\n }\n }\n };\n /**\n * @private\n * @param {Position} position position event.\n */\n Geolocation.prototype.positionChange_ = function (position) {\n var coords = position.coords;\n this.set(Property.ACCURACY, coords.accuracy);\n this.set(Property.ALTITUDE, coords.altitude === null ? undefined : coords.altitude);\n this.set(Property.ALTITUDE_ACCURACY, coords.altitudeAccuracy === null ?\n undefined : coords.altitudeAccuracy);\n this.set(Property.HEADING, coords.heading === null ?\n undefined : toRadians(coords.heading));\n if (!this.position_) {\n this.position_ = [coords.longitude, coords.latitude];\n }\n else {\n this.position_[0] = coords.longitude;\n this.position_[1] = coords.latitude;\n }\n var projectedPosition = this.transform_(this.position_);\n this.set(Property.POSITION, projectedPosition);\n this.set(Property.SPEED, coords.speed === null ? undefined : coords.speed);\n var geometry = circularPolygon(this.position_, coords.accuracy);\n geometry.applyTransform(this.transform_);\n this.set(Property.ACCURACY_GEOMETRY, geometry);\n this.changed();\n };\n /**\n * @private\n * @param {PositionError} error error object.\n */\n Geolocation.prototype.positionError_ = function (error) {\n this.setTracking(false);\n this.dispatchEvent(new GeolocationError(error));\n };\n /**\n * Get the accuracy of the position in meters.\n * @return {number|undefined} The accuracy of the position measurement in\n * meters.\n * @observable\n * @api\n */\n Geolocation.prototype.getAccuracy = function () {\n return /** @type {number|undefined} */ (this.get(Property.ACCURACY));\n };\n /**\n * Get a geometry of the position accuracy.\n * @return {?import(\"./geom/Polygon.js\").default} A geometry of the position accuracy.\n * @observable\n * @api\n */\n Geolocation.prototype.getAccuracyGeometry = function () {\n return (\n /** @type {?import(\"./geom/Polygon.js\").default} */ (this.get(Property.ACCURACY_GEOMETRY) || null));\n };\n /**\n * Get the altitude associated with the position.\n * @return {number|undefined} The altitude of the position in meters above mean\n * sea level.\n * @observable\n * @api\n */\n Geolocation.prototype.getAltitude = function () {\n return /** @type {number|undefined} */ (this.get(Property.ALTITUDE));\n };\n /**\n * Get the altitude accuracy of the position.\n * @return {number|undefined} The accuracy of the altitude measurement in\n * meters.\n * @observable\n * @api\n */\n Geolocation.prototype.getAltitudeAccuracy = function () {\n return /** @type {number|undefined} */ (this.get(Property.ALTITUDE_ACCURACY));\n };\n /**\n * Get the heading as radians clockwise from North.\n * Note: depending on the browser, the heading is only defined if the `enableHighAccuracy`\n * is set to `true` in the tracking options.\n * @return {number|undefined} The heading of the device in radians from north.\n * @observable\n * @api\n */\n Geolocation.prototype.getHeading = function () {\n return /** @type {number|undefined} */ (this.get(Property.HEADING));\n };\n /**\n * Get the position of the device.\n * @return {import(\"./coordinate.js\").Coordinate|undefined} The current position of the device reported\n * in the current projection.\n * @observable\n * @api\n */\n Geolocation.prototype.getPosition = function () {\n return (\n /** @type {import(\"./coordinate.js\").Coordinate|undefined} */ (this.get(Property.POSITION)));\n };\n /**\n * Get the projection associated with the position.\n * @return {import(\"./proj/Projection.js\").default|undefined} The projection the position is\n * reported in.\n * @observable\n * @api\n */\n Geolocation.prototype.getProjection = function () {\n return (\n /** @type {import(\"./proj/Projection.js\").default|undefined} */ (this.get(Property.PROJECTION)));\n };\n /**\n * Get the speed in meters per second.\n * @return {number|undefined} The instantaneous speed of the device in meters\n * per second.\n * @observable\n * @api\n */\n Geolocation.prototype.getSpeed = function () {\n return /** @type {number|undefined} */ (this.get(Property.SPEED));\n };\n /**\n * Determine if the device location is being tracked.\n * @return {boolean} The device location is being tracked.\n * @observable\n * @api\n */\n Geolocation.prototype.getTracking = function () {\n return /** @type {boolean} */ (this.get(Property.TRACKING));\n };\n /**\n * Get the tracking options.\n * See http://www.w3.org/TR/geolocation-API/#position-options.\n * @return {PositionOptions|undefined} PositionOptions as defined by\n * the [HTML5 Geolocation spec\n * ](http://www.w3.org/TR/geolocation-API/#position_options_interface).\n * @observable\n * @api\n */\n Geolocation.prototype.getTrackingOptions = function () {\n return /** @type {PositionOptions|undefined} */ (this.get(Property.TRACKING_OPTIONS));\n };\n /**\n * Set the projection to use for transforming the coordinates.\n * @param {import(\"./proj.js\").ProjectionLike} projection The projection the position is\n * reported in.\n * @observable\n * @api\n */\n Geolocation.prototype.setProjection = function (projection) {\n this.set(Property.PROJECTION, getProjection(projection));\n };\n /**\n * Enable or disable tracking.\n * @param {boolean} tracking Enable tracking.\n * @observable\n * @api\n */\n Geolocation.prototype.setTracking = function (tracking) {\n this.set(Property.TRACKING, tracking);\n };\n /**\n * Set the tracking options.\n * See http://www.w3.org/TR/geolocation-API/#position-options.\n * @param {PositionOptions} options PositionOptions as defined by the\n * [HTML5 Geolocation spec\n * ](http://www.w3.org/TR/geolocation-API/#position_options_interface).\n * @observable\n * @api\n */\n Geolocation.prototype.setTrackingOptions = function (options) {\n this.set(Property.TRACKING_OPTIONS, options);\n };\n return Geolocation;\n}(BaseObject));\nexport default Geolocation;\n//# sourceMappingURL=Geolocation.js.map","/**\n * @module ol/Kinetic\n */\n/**\n * @classdesc\n * Implementation of inertial deceleration for map movement.\n *\n * @api\n */\nvar Kinetic = /** @class */ (function () {\n /**\n * @param {number} decay Rate of decay (must be negative).\n * @param {number} minVelocity Minimum velocity (pixels/millisecond).\n * @param {number} delay Delay to consider to calculate the kinetic\n * initial values (milliseconds).\n */\n function Kinetic(decay, minVelocity, delay) {\n /**\n * @private\n * @type {number}\n */\n this.decay_ = decay;\n /**\n * @private\n * @type {number}\n */\n this.minVelocity_ = minVelocity;\n /**\n * @private\n * @type {number}\n */\n this.delay_ = delay;\n /**\n * @private\n * @type {Array}\n */\n this.points_ = [];\n /**\n * @private\n * @type {number}\n */\n this.angle_ = 0;\n /**\n * @private\n * @type {number}\n */\n this.initialVelocity_ = 0;\n }\n /**\n * FIXME empty description for jsdoc\n */\n Kinetic.prototype.begin = function () {\n this.points_.length = 0;\n this.angle_ = 0;\n this.initialVelocity_ = 0;\n };\n /**\n * @param {number} x X.\n * @param {number} y Y.\n */\n Kinetic.prototype.update = function (x, y) {\n this.points_.push(x, y, Date.now());\n };\n /**\n * @return {boolean} Whether we should do kinetic animation.\n */\n Kinetic.prototype.end = function () {\n if (this.points_.length < 6) {\n // at least 2 points are required (i.e. there must be at least 6 elements\n // in the array)\n return false;\n }\n var delay = Date.now() - this.delay_;\n var lastIndex = this.points_.length - 3;\n if (this.points_[lastIndex + 2] < delay) {\n // the last tracked point is too old, which means that the user stopped\n // panning before releasing the map\n return false;\n }\n // get the first point which still falls into the delay time\n var firstIndex = lastIndex - 3;\n while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) {\n firstIndex -= 3;\n }\n var duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2];\n // we don't want a duration of 0 (divide by zero)\n // we also make sure the user panned for a duration of at least one frame\n // (1/60s) to compute sane displacement values\n if (duration < 1000 / 60) {\n return false;\n }\n var dx = this.points_[lastIndex] - this.points_[firstIndex];\n var dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1];\n this.angle_ = Math.atan2(dy, dx);\n this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration;\n return this.initialVelocity_ > this.minVelocity_;\n };\n /**\n * @return {number} Total distance travelled (pixels).\n */\n Kinetic.prototype.getDistance = function () {\n return (this.minVelocity_ - this.initialVelocity_) / this.decay_;\n };\n /**\n * @return {number} Angle of the kinetic panning animation (radians).\n */\n Kinetic.prototype.getAngle = function () {\n return this.angle_;\n };\n return Kinetic;\n}());\nexport default Kinetic;\n//# sourceMappingURL=Kinetic.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/MapEvent\n */\nimport Event from './events/Event.js';\n/**\n * @classdesc\n * Events emitted as map events are instances of this type.\n * See {@link module:ol/PluggableMap~PluggableMap} for which events trigger a map event.\n */\nvar MapEvent = /** @class */ (function (_super) {\n __extends(MapEvent, _super);\n /**\n * @param {string} type Event type.\n * @param {import(\"./PluggableMap.js\").default} map Map.\n * @param {?import(\"./PluggableMap.js\").FrameState=} opt_frameState Frame state.\n */\n function MapEvent(type, map, opt_frameState) {\n var _this = _super.call(this, type) || this;\n /**\n * The map where the event occurred.\n * @type {import(\"./PluggableMap.js\").default}\n * @api\n */\n _this.map = map;\n /**\n * The frame state at the time of the event.\n * @type {?import(\"./PluggableMap.js\").FrameState}\n * @api\n */\n _this.frameState = opt_frameState !== undefined ? opt_frameState : null;\n return _this;\n }\n return MapEvent;\n}(Event));\nexport default MapEvent;\n//# sourceMappingURL=MapEvent.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/MapBrowserEvent\n */\nimport MapEvent from './MapEvent.js';\n/**\n * @classdesc\n * Events emitted as map browser events are instances of this type.\n * See {@link module:ol/PluggableMap~PluggableMap} for which events trigger a map browser event.\n */\nvar MapBrowserEvent = /** @class */ (function (_super) {\n __extends(MapBrowserEvent, _super);\n /**\n * @param {string} type Event type.\n * @param {import(\"./PluggableMap.js\").default} map Map.\n * @param {Event} browserEvent Browser event.\n * @param {boolean=} opt_dragging Is the map currently being dragged?\n * @param {?import(\"./PluggableMap.js\").FrameState=} opt_frameState Frame state.\n */\n function MapBrowserEvent(type, map, browserEvent, opt_dragging, opt_frameState) {\n var _this = _super.call(this, type, map, opt_frameState) || this;\n /**\n * The original browser event.\n * @const\n * @type {Event}\n * @api\n */\n _this.originalEvent = browserEvent;\n /**\n * The map pixel relative to the viewport corresponding to the original browser event.\n * @type {?import(\"./pixel.js\").Pixel}\n */\n _this.pixel_ = null;\n /**\n * The coordinate in the user projection corresponding to the original browser event.\n * @type {?import(\"./coordinate.js\").Coordinate}\n */\n _this.coordinate_ = null;\n /**\n * Indicates if the map is currently being dragged. Only set for\n * `POINTERDRAG` and `POINTERMOVE` events. Default is `false`.\n *\n * @type {boolean}\n * @api\n */\n _this.dragging = opt_dragging !== undefined ? opt_dragging : false;\n return _this;\n }\n Object.defineProperty(MapBrowserEvent.prototype, \"pixel\", {\n /**\n * The map pixel relative to the viewport corresponding to the original browser event.\n * @type {import(\"./pixel.js\").Pixel}\n * @api\n */\n get: function () {\n if (!this.pixel_) {\n this.pixel_ = this.map.getEventPixel(this.originalEvent);\n }\n return this.pixel_;\n },\n set: function (pixel) {\n this.pixel_ = pixel;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(MapBrowserEvent.prototype, \"coordinate\", {\n /**\n * The coordinate corresponding to the original browser event. This will be in the user\n * projection if one is set. Otherwise it will be in the view projection.\n * @type {import(\"./coordinate.js\").Coordinate}\n * @api\n */\n get: function () {\n if (!this.coordinate_) {\n this.coordinate_ = this.map.getCoordinateFromPixel(this.pixel);\n }\n return this.coordinate_;\n },\n set: function (coordinate) {\n this.coordinate_ = coordinate;\n },\n enumerable: true,\n configurable: true\n });\n /**\n * Prevents the default browser action.\n * See https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault.\n * @override\n * @api\n */\n MapBrowserEvent.prototype.preventDefault = function () {\n _super.prototype.preventDefault.call(this);\n this.originalEvent.preventDefault();\n };\n /**\n * Prevents further propagation of the current event.\n * See https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation.\n * @override\n * @api\n */\n MapBrowserEvent.prototype.stopPropagation = function () {\n _super.prototype.stopPropagation.call(this);\n this.originalEvent.stopPropagation();\n };\n return MapBrowserEvent;\n}(MapEvent));\nexport default MapBrowserEvent;\n//# sourceMappingURL=MapBrowserEvent.js.map","/**\n * @module ol/has\n */\nvar ua = typeof navigator !== 'undefined' ?\n navigator.userAgent.toLowerCase() : '';\n/**\n * User agent string says we are dealing with Firefox as browser.\n * @type {boolean}\n */\nexport var FIREFOX = ua.indexOf('firefox') !== -1;\n/**\n * User agent string says we are dealing with Safari as browser.\n * @type {boolean}\n */\nexport var SAFARI = ua.indexOf('safari') !== -1 && ua.indexOf('chrom') == -1;\n/**\n * User agent string says we are dealing with a WebKit engine.\n * @type {boolean}\n */\nexport var WEBKIT = ua.indexOf('webkit') !== -1 && ua.indexOf('edge') == -1;\n/**\n * User agent string says we are dealing with a Mac as platform.\n * @type {boolean}\n */\nexport var MAC = ua.indexOf('macintosh') !== -1;\n/**\n * The ratio between physical pixels and device-independent pixels\n * (dips) on the device (`window.devicePixelRatio`).\n * @const\n * @type {number}\n * @api\n */\nexport var DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;\n/**\n * Image.prototype.decode() is supported.\n * @type {boolean}\n */\nexport var IMAGE_DECODE = typeof Image !== 'undefined' && Image.prototype.decode;\n//# sourceMappingURL=has.js.map","/**\n * @module ol/MapBrowserEventType\n */\nimport EventType from './events/EventType.js';\n/**\n * Constants for event names.\n * @enum {string}\n */\nexport default {\n /**\n * A true single click with no dragging and no double click. Note that this\n * event is delayed by 250 ms to ensure that it is not a double click.\n * @event module:ol/MapBrowserEvent~MapBrowserEvent#singleclick\n * @api\n */\n SINGLECLICK: 'singleclick',\n /**\n * A click with no dragging. A double click will fire two of this.\n * @event module:ol/MapBrowserEvent~MapBrowserEvent#click\n * @api\n */\n CLICK: EventType.CLICK,\n /**\n * A true double click, with no dragging.\n * @event module:ol/MapBrowserEvent~MapBrowserEvent#dblclick\n * @api\n */\n DBLCLICK: EventType.DBLCLICK,\n /**\n * Triggered when a pointer is dragged.\n * @event module:ol/MapBrowserEvent~MapBrowserEvent#pointerdrag\n * @api\n */\n POINTERDRAG: 'pointerdrag',\n /**\n * Triggered when a pointer is moved. Note that on touch devices this is\n * triggered when the map is panned, so is not the same as mousemove.\n * @event module:ol/MapBrowserEvent~MapBrowserEvent#pointermove\n * @api\n */\n POINTERMOVE: 'pointermove',\n POINTERDOWN: 'pointerdown',\n POINTERUP: 'pointerup',\n POINTEROVER: 'pointerover',\n POINTEROUT: 'pointerout',\n POINTERENTER: 'pointerenter',\n POINTERLEAVE: 'pointerleave',\n POINTERCANCEL: 'pointercancel'\n};\n//# sourceMappingURL=MapBrowserEventType.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/MapBrowserPointerEvent\n */\nimport MapBrowserEvent from './MapBrowserEvent.js';\nvar MapBrowserPointerEvent = /** @class */ (function (_super) {\n __extends(MapBrowserPointerEvent, _super);\n /**\n * @param {string} type Event type.\n * @param {import(\"./PluggableMap.js\").default} map Map.\n * @param {PointerEvent} pointerEvent Pointer event.\n * @param {boolean=} opt_dragging Is the map currently being dragged?\n * @param {?import(\"./PluggableMap.js\").FrameState=} opt_frameState Frame state.\n */\n function MapBrowserPointerEvent(type, map, pointerEvent, opt_dragging, opt_frameState) {\n var _this = _super.call(this, type, map, pointerEvent, opt_dragging, opt_frameState) || this;\n /**\n * @const\n * @type {PointerEvent}\n */\n _this.pointerEvent = pointerEvent;\n return _this;\n }\n return MapBrowserPointerEvent;\n}(MapBrowserEvent));\nexport default MapBrowserPointerEvent;\n//# sourceMappingURL=MapBrowserPointerEvent.js.map","/**\n * @module ol/pointer/EventType\n */\n/**\n * Constants for event names.\n * @enum {string}\n */\nexport default {\n POINTERMOVE: 'pointermove',\n POINTERDOWN: 'pointerdown',\n POINTERUP: 'pointerup',\n POINTEROVER: 'pointerover',\n POINTEROUT: 'pointerout',\n POINTERENTER: 'pointerenter',\n POINTERLEAVE: 'pointerleave',\n POINTERCANCEL: 'pointercancel'\n};\n//# sourceMappingURL=EventType.js.map","/**\n * @module ol/MapBrowserEventHandler\n */\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nimport '@openlayers/pepjs';\nimport { DEVICE_PIXEL_RATIO } from './has.js';\nimport MapBrowserEventType from './MapBrowserEventType.js';\nimport MapBrowserPointerEvent from './MapBrowserPointerEvent.js';\nimport { listen, unlistenByKey } from './events.js';\nimport EventTarget from './events/Target.js';\nimport PointerEventType from './pointer/EventType.js';\nvar MapBrowserEventHandler = /** @class */ (function (_super) {\n __extends(MapBrowserEventHandler, _super);\n /**\n * @param {import(\"./PluggableMap.js\").default} map The map with the viewport to listen to events on.\n * @param {number=} moveTolerance The minimal distance the pointer must travel to trigger a move.\n */\n function MapBrowserEventHandler(map, moveTolerance) {\n var _this = _super.call(this, map) || this;\n /**\n * This is the element that we will listen to the real events on.\n * @type {import(\"./PluggableMap.js\").default}\n * @private\n */\n _this.map_ = map;\n /**\n * @type {any}\n * @private\n */\n _this.clickTimeoutId_;\n /**\n * @type {boolean}\n * @private\n */\n _this.dragging_ = false;\n /**\n * @type {!Array}\n * @private\n */\n _this.dragListenerKeys_ = [];\n /**\n * @type {number}\n * @private\n */\n _this.moveTolerance_ = moveTolerance ?\n moveTolerance * DEVICE_PIXEL_RATIO : DEVICE_PIXEL_RATIO;\n /**\n * The most recent \"down\" type event (or null if none have occurred).\n * Set on pointerdown.\n * @type {PointerEvent}\n * @private\n */\n _this.down_ = null;\n var element = _this.map_.getViewport();\n /**\n * @type {number}\n * @private\n */\n _this.activePointers_ = 0;\n /**\n * @type {!Object}\n * @private\n */\n _this.trackedTouches_ = {};\n _this.element_ = element;\n /**\n * @type {?import(\"./events.js\").EventsKey}\n * @private\n */\n _this.pointerdownListenerKey_ = listen(element, PointerEventType.POINTERDOWN, _this.handlePointerDown_, _this);\n /**\n * @type {?import(\"./events.js\").EventsKey}\n * @private\n */\n _this.relayedListenerKey_ = listen(element, PointerEventType.POINTERMOVE, _this.relayEvent_, _this);\n return _this;\n }\n /**\n * @param {PointerEvent} pointerEvent Pointer\n * event.\n * @private\n */\n MapBrowserEventHandler.prototype.emulateClick_ = function (pointerEvent) {\n var newEvent = new MapBrowserPointerEvent(MapBrowserEventType.CLICK, this.map_, pointerEvent);\n this.dispatchEvent(newEvent);\n if (this.clickTimeoutId_ !== undefined) {\n // double-click\n clearTimeout(this.clickTimeoutId_);\n this.clickTimeoutId_ = undefined;\n newEvent = new MapBrowserPointerEvent(MapBrowserEventType.DBLCLICK, this.map_, pointerEvent);\n this.dispatchEvent(newEvent);\n }\n else {\n // click\n this.clickTimeoutId_ = setTimeout(function () {\n this.clickTimeoutId_ = undefined;\n var newEvent = new MapBrowserPointerEvent(MapBrowserEventType.SINGLECLICK, this.map_, pointerEvent);\n this.dispatchEvent(newEvent);\n }.bind(this), 250);\n }\n };\n /**\n * Keeps track on how many pointers are currently active.\n *\n * @param {PointerEvent} pointerEvent Pointer\n * event.\n * @private\n */\n MapBrowserEventHandler.prototype.updateActivePointers_ = function (pointerEvent) {\n var event = pointerEvent;\n if (event.type == MapBrowserEventType.POINTERUP ||\n event.type == MapBrowserEventType.POINTERCANCEL) {\n delete this.trackedTouches_[event.pointerId];\n }\n else if (event.type == MapBrowserEventType.POINTERDOWN) {\n this.trackedTouches_[event.pointerId] = true;\n }\n this.activePointers_ = Object.keys(this.trackedTouches_).length;\n };\n /**\n * @param {PointerEvent} pointerEvent Pointer\n * event.\n * @private\n */\n MapBrowserEventHandler.prototype.handlePointerUp_ = function (pointerEvent) {\n this.updateActivePointers_(pointerEvent);\n var newEvent = new MapBrowserPointerEvent(MapBrowserEventType.POINTERUP, this.map_, pointerEvent);\n this.dispatchEvent(newEvent);\n // We emulate click events on left mouse button click, touch contact, and pen\n // contact. isMouseActionButton returns true in these cases (evt.button is set\n // to 0).\n // See http://www.w3.org/TR/pointerevents/#button-states\n // We only fire click, singleclick, and doubleclick if nobody has called\n // event.stopPropagation() or event.preventDefault().\n if (!newEvent.propagationStopped && !this.dragging_ && this.isMouseActionButton_(pointerEvent)) {\n this.emulateClick_(this.down_);\n }\n if (this.activePointers_ === 0) {\n this.dragListenerKeys_.forEach(unlistenByKey);\n this.dragListenerKeys_.length = 0;\n this.dragging_ = false;\n this.down_ = null;\n }\n };\n /**\n * @param {PointerEvent} pointerEvent Pointer\n * event.\n * @return {boolean} If the left mouse button was pressed.\n * @private\n */\n MapBrowserEventHandler.prototype.isMouseActionButton_ = function (pointerEvent) {\n return pointerEvent.button === 0;\n };\n /**\n * @param {PointerEvent} pointerEvent Pointer\n * event.\n * @private\n */\n MapBrowserEventHandler.prototype.handlePointerDown_ = function (pointerEvent) {\n this.updateActivePointers_(pointerEvent);\n var newEvent = new MapBrowserPointerEvent(MapBrowserEventType.POINTERDOWN, this.map_, pointerEvent);\n this.dispatchEvent(newEvent);\n this.down_ = pointerEvent;\n if (this.dragListenerKeys_.length === 0) {\n this.dragListenerKeys_.push(listen(document, MapBrowserEventType.POINTERMOVE, this.handlePointerMove_, this), listen(document, MapBrowserEventType.POINTERUP, this.handlePointerUp_, this), \n /* Note that the listener for `pointercancel is set up on\n * `pointerEventHandler_` and not `documentPointerEventHandler_` like\n * the `pointerup` and `pointermove` listeners.\n *\n * The reason for this is the following: `TouchSource.vacuumTouches_()`\n * issues `pointercancel` events, when there was no `touchend` for a\n * `touchstart`. Now, let's say a first `touchstart` is registered on\n * `pointerEventHandler_`. The `documentPointerEventHandler_` is set up.\n * But `documentPointerEventHandler_` doesn't know about the first\n * `touchstart`. If there is no `touchend` for the `touchstart`, we can\n * only receive a `touchcancel` from `pointerEventHandler_`, because it is\n * only registered there.\n */\n listen(this.element_, MapBrowserEventType.POINTERCANCEL, this.handlePointerUp_, this));\n }\n };\n /**\n * @param {PointerEvent} pointerEvent Pointer\n * event.\n * @private\n */\n MapBrowserEventHandler.prototype.handlePointerMove_ = function (pointerEvent) {\n // Between pointerdown and pointerup, pointermove events are triggered.\n // To avoid a 'false' touchmove event to be dispatched, we test if the pointer\n // moved a significant distance.\n if (this.isMoving_(pointerEvent)) {\n this.dragging_ = true;\n var newEvent = new MapBrowserPointerEvent(MapBrowserEventType.POINTERDRAG, this.map_, pointerEvent, this.dragging_);\n this.dispatchEvent(newEvent);\n }\n };\n /**\n * Wrap and relay a pointer event. Note that this requires that the type\n * string for the MapBrowserPointerEvent matches the PointerEvent type.\n * @param {PointerEvent} pointerEvent Pointer\n * event.\n * @private\n */\n MapBrowserEventHandler.prototype.relayEvent_ = function (pointerEvent) {\n var dragging = !!(this.down_ && this.isMoving_(pointerEvent));\n this.dispatchEvent(new MapBrowserPointerEvent(pointerEvent.type, this.map_, pointerEvent, dragging));\n };\n /**\n * @param {PointerEvent} pointerEvent Pointer\n * event.\n * @return {boolean} Is moving.\n * @private\n */\n MapBrowserEventHandler.prototype.isMoving_ = function (pointerEvent) {\n return this.dragging_ ||\n Math.abs(pointerEvent.clientX - this.down_.clientX) > this.moveTolerance_ ||\n Math.abs(pointerEvent.clientY - this.down_.clientY) > this.moveTolerance_;\n };\n /**\n * @inheritDoc\n */\n MapBrowserEventHandler.prototype.disposeInternal = function () {\n if (this.relayedListenerKey_) {\n unlistenByKey(this.relayedListenerKey_);\n this.relayedListenerKey_ = null;\n }\n if (this.pointerdownListenerKey_) {\n unlistenByKey(this.pointerdownListenerKey_);\n this.pointerdownListenerKey_ = null;\n }\n this.dragListenerKeys_.forEach(unlistenByKey);\n this.dragListenerKeys_.length = 0;\n this.element_ = null;\n _super.prototype.disposeInternal.call(this);\n };\n return MapBrowserEventHandler;\n}(EventTarget));\nexport default MapBrowserEventHandler;\n//# sourceMappingURL=MapBrowserEventHandler.js.map","/**\n * @module ol/MapEventType\n */\n/**\n * @enum {string}\n */\nexport default {\n /**\n * Triggered after a map frame is rendered.\n * @event module:ol/MapEvent~MapEvent#postrender\n * @api\n */\n POSTRENDER: 'postrender',\n /**\n * Triggered when the map starts moving.\n * @event module:ol/MapEvent~MapEvent#movestart\n * @api\n */\n MOVESTART: 'movestart',\n /**\n * Triggered after the map is moved.\n * @event module:ol/MapEvent~MapEvent#moveend\n * @api\n */\n MOVEEND: 'moveend'\n};\n//# sourceMappingURL=MapEventType.js.map","/**\n * @module ol/MapProperty\n */\n/**\n * @enum {string}\n */\nexport default {\n LAYERGROUP: 'layergroup',\n SIZE: 'size',\n TARGET: 'target',\n VIEW: 'view'\n};\n//# sourceMappingURL=MapProperty.js.map","/**\n * @module ol/render/EventType\n */\n/**\n * @enum {string}\n */\nexport default {\n /**\n * Triggered before a layer is rendered.\n * @event module:ol/render/Event~RenderEvent#prerender\n * @api\n */\n PRERENDER: 'prerender',\n /**\n * Triggered after a layer is rendered.\n * @event module:ol/render/Event~RenderEvent#postrender\n * @api\n */\n POSTRENDER: 'postrender',\n /**\n * Triggered before layers are rendered.\n * The event object will not have a `context` set.\n * @event module:ol/render/Event~RenderEvent#precompose\n * @api\n */\n PRECOMPOSE: 'precompose',\n /**\n * Triggered after all layers are rendered.\n * The event object will not have a `context` set.\n * @event module:ol/render/Event~RenderEvent#postcompose\n * @api\n */\n POSTCOMPOSE: 'postcompose',\n /**\n * Triggered when rendering is complete, i.e. all sources and tiles have\n * finished loading for the current viewport, and all tiles are faded in.\n * The event object will not have a `context` set.\n * @event module:ol/render/Event~RenderEvent#rendercomplete\n * @api\n */\n RENDERCOMPLETE: 'rendercomplete'\n};\n//# sourceMappingURL=EventType.js.map","/**\n * @module ol/TileState\n */\n/**\n * @enum {number}\n */\nexport default {\n IDLE: 0,\n LOADING: 1,\n LOADED: 2,\n /**\n * Indicates that tile loading failed\n * @type {number}\n */\n ERROR: 3,\n EMPTY: 4,\n ABORT: 5\n};\n//# sourceMappingURL=TileState.js.map","/**\n * @module ol/structs/PriorityQueue\n */\nimport { assert } from '../asserts.js';\nimport { clear } from '../obj.js';\n/**\n * @type {number}\n */\nexport var DROP = Infinity;\n/**\n * @classdesc\n * Priority queue.\n *\n * The implementation is inspired from the Closure Library's Heap class and\n * Python's heapq module.\n *\n * See http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html\n * and http://hg.python.org/cpython/file/2.7/Lib/heapq.py.\n *\n * @template T\n */\nvar PriorityQueue = /** @class */ (function () {\n /**\n * @param {function(T): number} priorityFunction Priority function.\n * @param {function(T): string} keyFunction Key function.\n */\n function PriorityQueue(priorityFunction, keyFunction) {\n /**\n * @type {function(T): number}\n * @private\n */\n this.priorityFunction_ = priorityFunction;\n /**\n * @type {function(T): string}\n * @private\n */\n this.keyFunction_ = keyFunction;\n /**\n * @type {Array}\n * @private\n */\n this.elements_ = [];\n /**\n * @type {Array}\n * @private\n */\n this.priorities_ = [];\n /**\n * @type {!Object}\n * @private\n */\n this.queuedElements_ = {};\n }\n /**\n * FIXME empty description for jsdoc\n */\n PriorityQueue.prototype.clear = function () {\n this.elements_.length = 0;\n this.priorities_.length = 0;\n clear(this.queuedElements_);\n };\n /**\n * Remove and return the highest-priority element. O(log N).\n * @return {T} Element.\n */\n PriorityQueue.prototype.dequeue = function () {\n var elements = this.elements_;\n var priorities = this.priorities_;\n var element = elements[0];\n if (elements.length == 1) {\n elements.length = 0;\n priorities.length = 0;\n }\n else {\n elements[0] = elements.pop();\n priorities[0] = priorities.pop();\n this.siftUp_(0);\n }\n var elementKey = this.keyFunction_(element);\n delete this.queuedElements_[elementKey];\n return element;\n };\n /**\n * Enqueue an element. O(log N).\n * @param {T} element Element.\n * @return {boolean} The element was added to the queue.\n */\n PriorityQueue.prototype.enqueue = function (element) {\n assert(!(this.keyFunction_(element) in this.queuedElements_), 31); // Tried to enqueue an `element` that was already added to the queue\n var priority = this.priorityFunction_(element);\n if (priority != DROP) {\n this.elements_.push(element);\n this.priorities_.push(priority);\n this.queuedElements_[this.keyFunction_(element)] = true;\n this.siftDown_(0, this.elements_.length - 1);\n return true;\n }\n return false;\n };\n /**\n * @return {number} Count.\n */\n PriorityQueue.prototype.getCount = function () {\n return this.elements_.length;\n };\n /**\n * Gets the index of the left child of the node at the given index.\n * @param {number} index The index of the node to get the left child for.\n * @return {number} The index of the left child.\n * @private\n */\n PriorityQueue.prototype.getLeftChildIndex_ = function (index) {\n return index * 2 + 1;\n };\n /**\n * Gets the index of the right child of the node at the given index.\n * @param {number} index The index of the node to get the right child for.\n * @return {number} The index of the right child.\n * @private\n */\n PriorityQueue.prototype.getRightChildIndex_ = function (index) {\n return index * 2 + 2;\n };\n /**\n * Gets the index of the parent of the node at the given index.\n * @param {number} index The index of the node to get the parent for.\n * @return {number} The index of the parent.\n * @private\n */\n PriorityQueue.prototype.getParentIndex_ = function (index) {\n return (index - 1) >> 1;\n };\n /**\n * Make this a heap. O(N).\n * @private\n */\n PriorityQueue.prototype.heapify_ = function () {\n var i;\n for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) {\n this.siftUp_(i);\n }\n };\n /**\n * @return {boolean} Is empty.\n */\n PriorityQueue.prototype.isEmpty = function () {\n return this.elements_.length === 0;\n };\n /**\n * @param {string} key Key.\n * @return {boolean} Is key queued.\n */\n PriorityQueue.prototype.isKeyQueued = function (key) {\n return key in this.queuedElements_;\n };\n /**\n * @param {T} element Element.\n * @return {boolean} Is queued.\n */\n PriorityQueue.prototype.isQueued = function (element) {\n return this.isKeyQueued(this.keyFunction_(element));\n };\n /**\n * @param {number} index The index of the node to move down.\n * @private\n */\n PriorityQueue.prototype.siftUp_ = function (index) {\n var elements = this.elements_;\n var priorities = this.priorities_;\n var count = elements.length;\n var element = elements[index];\n var priority = priorities[index];\n var startIndex = index;\n while (index < (count >> 1)) {\n var lIndex = this.getLeftChildIndex_(index);\n var rIndex = this.getRightChildIndex_(index);\n var smallerChildIndex = rIndex < count &&\n priorities[rIndex] < priorities[lIndex] ?\n rIndex : lIndex;\n elements[index] = elements[smallerChildIndex];\n priorities[index] = priorities[smallerChildIndex];\n index = smallerChildIndex;\n }\n elements[index] = element;\n priorities[index] = priority;\n this.siftDown_(startIndex, index);\n };\n /**\n * @param {number} startIndex The index of the root.\n * @param {number} index The index of the node to move up.\n * @private\n */\n PriorityQueue.prototype.siftDown_ = function (startIndex, index) {\n var elements = this.elements_;\n var priorities = this.priorities_;\n var element = elements[index];\n var priority = priorities[index];\n while (index > startIndex) {\n var parentIndex = this.getParentIndex_(index);\n if (priorities[parentIndex] > priority) {\n elements[index] = elements[parentIndex];\n priorities[index] = priorities[parentIndex];\n index = parentIndex;\n }\n else {\n break;\n }\n }\n elements[index] = element;\n priorities[index] = priority;\n };\n /**\n * FIXME empty description for jsdoc\n */\n PriorityQueue.prototype.reprioritize = function () {\n var priorityFunction = this.priorityFunction_;\n var elements = this.elements_;\n var priorities = this.priorities_;\n var index = 0;\n var n = elements.length;\n var element, i, priority;\n for (i = 0; i < n; ++i) {\n element = elements[i];\n priority = priorityFunction(element);\n if (priority == DROP) {\n delete this.queuedElements_[this.keyFunction_(element)];\n }\n else {\n priorities[index] = priority;\n elements[index++] = element;\n }\n }\n elements.length = index;\n priorities.length = index;\n this.heapify_();\n };\n return PriorityQueue;\n}());\nexport default PriorityQueue;\n//# sourceMappingURL=PriorityQueue.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/TileQueue\n */\nimport TileState from './TileState.js';\nimport EventType from './events/EventType.js';\nimport PriorityQueue from './structs/PriorityQueue.js';\n/**\n * @typedef {function(import(\"./Tile.js\").default, string, import(\"./coordinate.js\").Coordinate, number): number} PriorityFunction\n */\nvar TileQueue = /** @class */ (function (_super) {\n __extends(TileQueue, _super);\n /**\n * @param {PriorityFunction} tilePriorityFunction Tile priority function.\n * @param {function(): ?} tileChangeCallback Function called on each tile change event.\n */\n function TileQueue(tilePriorityFunction, tileChangeCallback) {\n var _this = _super.call(this, \n /**\n * @param {Array} element Element.\n * @return {number} Priority.\n */\n function (element) {\n return tilePriorityFunction.apply(null, element);\n }, \n /**\n * @param {Array} element Element.\n * @return {string} Key.\n */\n function (element) {\n return ( /** @type {import(\"./Tile.js\").default} */(element[0]).getKey());\n }) || this;\n /** @private */\n _this.boundHandleTileChange_ = _this.handleTileChange.bind(_this);\n /**\n * @private\n * @type {function(): ?}\n */\n _this.tileChangeCallback_ = tileChangeCallback;\n /**\n * @private\n * @type {number}\n */\n _this.tilesLoading_ = 0;\n /**\n * @private\n * @type {!Object}\n */\n _this.tilesLoadingKeys_ = {};\n return _this;\n }\n /**\n * @inheritDoc\n */\n TileQueue.prototype.enqueue = function (element) {\n var added = _super.prototype.enqueue.call(this, element);\n if (added) {\n var tile = element[0];\n tile.addEventListener(EventType.CHANGE, this.boundHandleTileChange_);\n }\n return added;\n };\n /**\n * @return {number} Number of tiles loading.\n */\n TileQueue.prototype.getTilesLoading = function () {\n return this.tilesLoading_;\n };\n /**\n * @param {import(\"./events/Event.js\").default} event Event.\n * @protected\n */\n TileQueue.prototype.handleTileChange = function (event) {\n var tile = /** @type {import(\"./Tile.js\").default} */ (event.target);\n var state = tile.getState();\n if (tile.hifi && state === TileState.LOADED || state === TileState.ERROR ||\n state === TileState.EMPTY || state === TileState.ABORT) {\n tile.removeEventListener(EventType.CHANGE, this.boundHandleTileChange_);\n var tileKey = tile.getKey();\n if (tileKey in this.tilesLoadingKeys_) {\n delete this.tilesLoadingKeys_[tileKey];\n --this.tilesLoading_;\n }\n this.tileChangeCallback_();\n }\n };\n /**\n * @param {number} maxTotalLoading Maximum number tiles to load simultaneously.\n * @param {number} maxNewLoads Maximum number of new tiles to load.\n */\n TileQueue.prototype.loadMoreTiles = function (maxTotalLoading, maxNewLoads) {\n var newLoads = 0;\n var abortedTiles = false;\n var state, tile, tileKey;\n while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads &&\n this.getCount() > 0) {\n tile = /** @type {import(\"./Tile.js\").default} */ (this.dequeue()[0]);\n tileKey = tile.getKey();\n state = tile.getState();\n if (state === TileState.ABORT) {\n abortedTiles = true;\n }\n else if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) {\n this.tilesLoadingKeys_[tileKey] = true;\n ++this.tilesLoading_;\n ++newLoads;\n tile.load();\n }\n }\n if (newLoads === 0 && abortedTiles) {\n // Do not stop the render loop when all wanted tiles were aborted due to\n // a small, saturated tile cache.\n this.tileChangeCallback_();\n }\n };\n return TileQueue;\n}(PriorityQueue));\nexport default TileQueue;\n//# sourceMappingURL=TileQueue.js.map","/**\n * @module ol/tilegrid/common\n */\n/**\n * Default maximum zoom for default tile grids.\n * @type {number}\n */\nexport var DEFAULT_MAX_ZOOM = 42;\n/**\n * Default tile size.\n * @type {number}\n */\nexport var DEFAULT_TILE_SIZE = 256;\n//# sourceMappingURL=common.js.map","/**\n * @module ol/centerconstraint\n */\nimport { clamp } from './math.js';\n/**\n * @typedef {function((import(\"./coordinate.js\").Coordinate|undefined), number, import(\"./size.js\").Size, boolean=): (import(\"./coordinate.js\").Coordinate|undefined)} Type\n */\n/**\n * @param {import(\"./extent.js\").Extent} extent Extent.\n * @param {boolean} onlyCenter If true, the constraint will only apply to the view center.\n * @param {boolean} smooth If true, the view will be able to go slightly out of the given extent\n * (only during interaction and animation).\n * @return {Type} The constraint.\n */\nexport function createExtent(extent, onlyCenter, smooth) {\n return (\n /**\n * @param {import(\"./coordinate.js\").Coordinate|undefined} center Center.\n * @param {number} resolution Resolution.\n * @param {import(\"./size.js\").Size} size Viewport size; unused if `onlyCenter` was specified.\n * @param {boolean=} opt_isMoving True if an interaction or animation is in progress.\n * @return {import(\"./coordinate.js\").Coordinate|undefined} Center.\n */\n function (center, resolution, size, opt_isMoving) {\n if (center) {\n var viewWidth = onlyCenter ? 0 : size[0] * resolution;\n var viewHeight = onlyCenter ? 0 : size[1] * resolution;\n var minX = extent[0] + viewWidth / 2;\n var maxX = extent[2] - viewWidth / 2;\n var minY = extent[1] + viewHeight / 2;\n var maxY = extent[3] - viewHeight / 2;\n // note: when zooming out of bounds, min and max values for x and y may\n // end up inverted (min > max); this has to be accounted for\n if (minX > maxX) {\n minX = (maxX + minX) / 2;\n maxX = minX;\n }\n if (minY > maxY) {\n minY = (maxY + minY) / 2;\n maxY = minY;\n }\n var x = clamp(center[0], minX, maxX);\n var y = clamp(center[1], minY, maxY);\n var ratio = 30 * resolution;\n // during an interaction, allow some overscroll\n if (opt_isMoving && smooth) {\n x += -ratio * Math.log(1 + Math.max(0, minX - center[0]) / ratio) +\n ratio * Math.log(1 + Math.max(0, center[0] - maxX) / ratio);\n y += -ratio * Math.log(1 + Math.max(0, minY - center[1]) / ratio) +\n ratio * Math.log(1 + Math.max(0, center[1] - maxY) / ratio);\n }\n return [x, y];\n }\n else {\n return undefined;\n }\n });\n}\n/**\n * @param {import(\"./coordinate.js\").Coordinate=} center Center.\n * @return {import(\"./coordinate.js\").Coordinate|undefined} Center.\n */\nexport function none(center) {\n return center;\n}\n//# sourceMappingURL=centerconstraint.js.map","/**\n * @module ol/resolutionconstraint\n */\nimport { linearFindNearest } from './array.js';\nimport { getHeight, getWidth } from './extent.js';\nimport { clamp } from './math.js';\n/**\n * @typedef {function((number|undefined), number, import(\"./size.js\").Size, boolean=): (number|undefined)} Type\n */\n/**\n * Returns a modified resolution taking into acocunt the viewport size and maximum\n * allowed extent.\n * @param {number} resolution Resolution\n * @param {import(\"./extent.js\").Extent=} maxExtent Maximum allowed extent.\n * @param {import(\"./size.js\").Size} viewportSize Viewport size.\n * @return {number} Capped resolution.\n */\nfunction getViewportClampedResolution(resolution, maxExtent, viewportSize) {\n var xResolution = getWidth(maxExtent) / viewportSize[0];\n var yResolution = getHeight(maxExtent) / viewportSize[1];\n return Math.min(resolution, Math.min(xResolution, yResolution));\n}\n/**\n * Returns a modified resolution to be between maxResolution and minResolution while\n * still allowing the value to be slightly out of bounds.\n * Note: the computation is based on the logarithm function (ln):\n * - at 1, ln(x) is 0\n * - above 1, ln(x) keeps increasing but at a much slower pace than x\n * The final result is clamped to prevent getting too far away from bounds.\n * @param {number} resolution Resolution.\n * @param {number} maxResolution Max resolution.\n * @param {number} minResolution Min resolution.\n * @return {number} Smoothed resolution.\n */\nfunction getSmoothClampedResolution(resolution, maxResolution, minResolution) {\n var result = Math.min(resolution, maxResolution);\n var ratio = 50;\n result *= Math.log(1 + ratio * Math.max(0, resolution / maxResolution - 1)) / ratio + 1;\n if (minResolution) {\n result = Math.max(result, minResolution);\n result /= Math.log(1 + ratio * Math.max(0, minResolution / resolution - 1)) / ratio + 1;\n }\n return clamp(result, minResolution / 2, maxResolution * 2);\n}\n/**\n * @param {Array} resolutions Resolutions.\n * @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true.\n * @param {import(\"./extent.js\").Extent=} opt_maxExtent Maximum allowed extent.\n * @return {Type} Zoom function.\n */\nexport function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent) {\n return (\n /**\n * @param {number|undefined} resolution Resolution.\n * @param {number} direction Direction.\n * @param {import(\"./size.js\").Size} size Viewport size.\n * @param {boolean=} opt_isMoving True if an interaction or animation is in progress.\n * @return {number|undefined} Resolution.\n */\n function (resolution, direction, size, opt_isMoving) {\n if (resolution !== undefined) {\n var maxResolution = resolutions[0];\n var minResolution = resolutions[resolutions.length - 1];\n var cappedMaxRes = opt_maxExtent ?\n getViewportClampedResolution(maxResolution, opt_maxExtent, size) :\n maxResolution;\n // during interacting or animating, allow intermediary values\n if (opt_isMoving) {\n var smooth = opt_smooth !== undefined ? opt_smooth : true;\n if (!smooth) {\n return clamp(resolution, minResolution, cappedMaxRes);\n }\n return getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);\n }\n var capped = Math.min(cappedMaxRes, resolution);\n var z = Math.floor(linearFindNearest(resolutions, capped, direction));\n if (resolutions[z] > cappedMaxRes && z < resolutions.length - 1) {\n return resolutions[z + 1];\n }\n return resolutions[z];\n }\n else {\n return undefined;\n }\n });\n}\n/**\n * @param {number} power Power.\n * @param {number} maxResolution Maximum resolution.\n * @param {number=} opt_minResolution Minimum resolution.\n * @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true.\n * @param {import(\"./extent.js\").Extent=} opt_maxExtent Maximum allowed extent.\n * @return {Type} Zoom function.\n */\nexport function createSnapToPower(power, maxResolution, opt_minResolution, opt_smooth, opt_maxExtent) {\n return (\n /**\n * @param {number|undefined} resolution Resolution.\n * @param {number} direction Direction.\n * @param {import(\"./size.js\").Size} size Viewport size.\n * @param {boolean=} opt_isMoving True if an interaction or animation is in progress.\n * @return {number|undefined} Resolution.\n */\n function (resolution, direction, size, opt_isMoving) {\n if (resolution !== undefined) {\n var cappedMaxRes = opt_maxExtent ?\n getViewportClampedResolution(maxResolution, opt_maxExtent, size) :\n maxResolution;\n var minResolution = opt_minResolution !== undefined ? opt_minResolution : 0;\n // during interacting or animating, allow intermediary values\n if (opt_isMoving) {\n var smooth = opt_smooth !== undefined ? opt_smooth : true;\n if (!smooth) {\n return clamp(resolution, minResolution, cappedMaxRes);\n }\n return getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);\n }\n var tolerance = 1e-9;\n var minZoomLevel = Math.ceil(Math.log(maxResolution / cappedMaxRes) / Math.log(power) - tolerance);\n var offset = -direction * (0.5 - tolerance) + 0.5;\n var capped = Math.min(cappedMaxRes, resolution);\n var cappedZoomLevel = Math.floor(Math.log(maxResolution / capped) / Math.log(power) + offset);\n var zoomLevel = Math.max(minZoomLevel, cappedZoomLevel);\n var newResolution = maxResolution / Math.pow(power, zoomLevel);\n return clamp(newResolution, minResolution, cappedMaxRes);\n }\n else {\n return undefined;\n }\n });\n}\n/**\n * @param {number} maxResolution Max resolution.\n * @param {number} minResolution Min resolution.\n * @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true.\n * @param {import(\"./extent.js\").Extent=} opt_maxExtent Maximum allowed extent.\n * @return {Type} Zoom function.\n */\nexport function createMinMaxResolution(maxResolution, minResolution, opt_smooth, opt_maxExtent) {\n return (\n /**\n * @param {number|undefined} resolution Resolution.\n * @param {number} direction Direction.\n * @param {import(\"./size.js\").Size} size Viewport size.\n * @param {boolean=} opt_isMoving True if an interaction or animation is in progress.\n * @return {number|undefined} Resolution.\n */\n function (resolution, direction, size, opt_isMoving) {\n if (resolution !== undefined) {\n var cappedMaxRes = opt_maxExtent ?\n getViewportClampedResolution(maxResolution, opt_maxExtent, size) :\n maxResolution;\n var smooth = opt_smooth !== undefined ? opt_smooth : true;\n if (!smooth || !opt_isMoving) {\n return clamp(resolution, minResolution, cappedMaxRes);\n }\n return getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);\n }\n else {\n return undefined;\n }\n });\n}\n//# sourceMappingURL=resolutionconstraint.js.map","/**\n * @module ol/rotationconstraint\n */\nimport { toRadians } from './math.js';\n/**\n * @typedef {function((number|undefined), boolean=): (number|undefined)} Type\n */\n/**\n * @param {number|undefined} rotation Rotation.\n * @return {number|undefined} Rotation.\n */\nexport function disable(rotation) {\n if (rotation !== undefined) {\n return 0;\n }\n else {\n return undefined;\n }\n}\n/**\n * @param {number|undefined} rotation Rotation.\n * @return {number|undefined} Rotation.\n */\nexport function none(rotation) {\n if (rotation !== undefined) {\n return rotation;\n }\n else {\n return undefined;\n }\n}\n/**\n * @param {number} n N.\n * @return {Type} Rotation constraint.\n */\nexport function createSnapToN(n) {\n var theta = 2 * Math.PI / n;\n return (\n /**\n * @param {number|undefined} rotation Rotation.\n * @param {boolean=} opt_isMoving True if an interaction or animation is in progress.\n * @return {number|undefined} Rotation.\n */\n function (rotation, opt_isMoving) {\n if (opt_isMoving) {\n return rotation;\n }\n if (rotation !== undefined) {\n rotation = Math.floor(rotation / theta + 0.5) * theta;\n return rotation;\n }\n else {\n return undefined;\n }\n });\n}\n/**\n * @param {number=} opt_tolerance Tolerance.\n * @return {Type} Rotation constraint.\n */\nexport function createSnapToZero(opt_tolerance) {\n var tolerance = opt_tolerance || toRadians(5);\n return (\n /**\n * @param {number|undefined} rotation Rotation.\n * @param {boolean} opt_isMoving True if an interaction or animation is in progress.\n * @return {number|undefined} Rotation.\n */\n function (rotation, opt_isMoving) {\n if (opt_isMoving) {\n return rotation;\n }\n if (rotation !== undefined) {\n if (Math.abs(rotation) <= tolerance) {\n return 0;\n }\n else {\n return rotation;\n }\n }\n else {\n return undefined;\n }\n });\n}\n//# sourceMappingURL=rotationconstraint.js.map","/**\n * @module ol/ViewHint\n */\n/**\n * @enum {number}\n */\nexport default {\n ANIMATING: 0,\n INTERACTING: 1\n};\n//# sourceMappingURL=ViewHint.js.map","/**\n * @module ol/ViewProperty\n */\n/**\n * @enum {string}\n */\nexport default {\n CENTER: 'center',\n RESOLUTION: 'resolution',\n ROTATION: 'rotation'\n};\n//# sourceMappingURL=ViewProperty.js.map","/**\n * @module ol/string\n */\n/**\n * @param {number} number Number to be formatted\n * @param {number} width The desired width\n * @param {number=} opt_precision Precision of the output string (i.e. number of decimal places)\n * @returns {string} Formatted string\n */\nexport function padNumber(number, width, opt_precision) {\n var numberString = opt_precision !== undefined ? number.toFixed(opt_precision) : '' + number;\n var decimal = numberString.indexOf('.');\n decimal = decimal === -1 ? numberString.length : decimal;\n return decimal > width ? numberString : new Array(1 + width - decimal).join('0') + numberString;\n}\n/**\n * Adapted from https://github.com/omichelsen/compare-versions/blob/master/index.js\n * @param {string|number} v1 First version\n * @param {string|number} v2 Second version\n * @returns {number} Value\n */\nexport function compareVersions(v1, v2) {\n var s1 = ('' + v1).split('.');\n var s2 = ('' + v2).split('.');\n for (var i = 0; i < Math.max(s1.length, s2.length); i++) {\n var n1 = parseInt(s1[i] || '0', 10);\n var n2 = parseInt(s2[i] || '0', 10);\n if (n1 > n2) {\n return 1;\n }\n if (n2 > n1) {\n return -1;\n }\n }\n return 0;\n}\n//# sourceMappingURL=string.js.map","/**\n * @module ol/coordinate\n */\nimport { modulo } from './math.js';\nimport { padNumber } from './string.js';\n/**\n * An array of numbers representing an xy coordinate. Example: `[16, 48]`.\n * @typedef {Array} Coordinate\n * @api\n */\n/**\n * A function that takes a {@link module:ol/coordinate~Coordinate} and\n * transforms it into a `{string}`.\n *\n * @typedef {function((Coordinate|undefined)): string} CoordinateFormat\n * @api\n */\n/**\n * Add `delta` to `coordinate`. `coordinate` is modified in place and returned\n * by the function.\n *\n * Example:\n *\n * import {add} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * add(coord, [-2, 4]);\n * // coord is now [5.85, 51.983333]\n *\n * @param {Coordinate} coordinate Coordinate.\n * @param {Coordinate} delta Delta.\n * @return {Coordinate} The input coordinate adjusted by\n * the given delta.\n * @api\n */\nexport function add(coordinate, delta) {\n coordinate[0] += +delta[0];\n coordinate[1] += +delta[1];\n return coordinate;\n}\n/**\n * Calculates the point closest to the passed coordinate on the passed circle.\n *\n * @param {Coordinate} coordinate The coordinate.\n * @param {import(\"./geom/Circle.js\").default} circle The circle.\n * @return {Coordinate} Closest point on the circumference.\n */\nexport function closestOnCircle(coordinate, circle) {\n var r = circle.getRadius();\n var center = circle.getCenter();\n var x0 = center[0];\n var y0 = center[1];\n var x1 = coordinate[0];\n var y1 = coordinate[1];\n var dx = x1 - x0;\n var dy = y1 - y0;\n if (dx === 0 && dy === 0) {\n dx = 1;\n }\n var d = Math.sqrt(dx * dx + dy * dy);\n var x = x0 + r * dx / d;\n var y = y0 + r * dy / d;\n return [x, y];\n}\n/**\n * Calculates the point closest to the passed coordinate on the passed segment.\n * This is the foot of the perpendicular of the coordinate to the segment when\n * the foot is on the segment, or the closest segment coordinate when the foot\n * is outside the segment.\n *\n * @param {Coordinate} coordinate The coordinate.\n * @param {Array} segment The two coordinates\n * of the segment.\n * @return {Coordinate} The foot of the perpendicular of\n * the coordinate to the segment.\n */\nexport function closestOnSegment(coordinate, segment) {\n var x0 = coordinate[0];\n var y0 = coordinate[1];\n var start = segment[0];\n var end = segment[1];\n var x1 = start[0];\n var y1 = start[1];\n var x2 = end[0];\n var y2 = end[1];\n var dx = x2 - x1;\n var dy = y2 - y1;\n var along = (dx === 0 && dy === 0) ? 0 :\n ((dx * (x0 - x1)) + (dy * (y0 - y1))) / ((dx * dx + dy * dy) || 0);\n var x, y;\n if (along <= 0) {\n x = x1;\n y = y1;\n }\n else if (along >= 1) {\n x = x2;\n y = y2;\n }\n else {\n x = x1 + along * dx;\n y = y1 + along * dy;\n }\n return [x, y];\n}\n/**\n * Returns a {@link module:ol/coordinate~CoordinateFormat} function that can be\n * used to format\n * a {Coordinate} to a string.\n *\n * Example without specifying the fractional digits:\n *\n * import {createStringXY} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * var stringifyFunc = createStringXY();\n * var out = stringifyFunc(coord);\n * // out is now '8, 48'\n *\n * Example with explicitly specifying 2 fractional digits:\n *\n * import {createStringXY} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * var stringifyFunc = createStringXY(2);\n * var out = stringifyFunc(coord);\n * // out is now '7.85, 47.98'\n *\n * @param {number=} opt_fractionDigits The number of digits to include\n * after the decimal point. Default is `0`.\n * @return {CoordinateFormat} Coordinate format.\n * @api\n */\nexport function createStringXY(opt_fractionDigits) {\n return (\n /**\n * @param {Coordinate} coordinate Coordinate.\n * @return {string} String XY.\n */\n function (coordinate) {\n return toStringXY(coordinate, opt_fractionDigits);\n });\n}\n/**\n * @param {string} hemispheres Hemispheres.\n * @param {number} degrees Degrees.\n * @param {number=} opt_fractionDigits The number of digits to include\n * after the decimal point. Default is `0`.\n * @return {string} String.\n */\nexport function degreesToStringHDMS(hemispheres, degrees, opt_fractionDigits) {\n var normalizedDegrees = modulo(degrees + 180, 360) - 180;\n var x = Math.abs(3600 * normalizedDegrees);\n var dflPrecision = opt_fractionDigits || 0;\n var precision = Math.pow(10, dflPrecision);\n var deg = Math.floor(x / 3600);\n var min = Math.floor((x - deg * 3600) / 60);\n var sec = x - (deg * 3600) - (min * 60);\n sec = Math.ceil(sec * precision) / precision;\n if (sec >= 60) {\n sec = 0;\n min += 1;\n }\n if (min >= 60) {\n min = 0;\n deg += 1;\n }\n return deg + '\\u00b0 ' + padNumber(min, 2) + '\\u2032 ' +\n padNumber(sec, 2, dflPrecision) + '\\u2033' +\n (normalizedDegrees == 0 ? '' : ' ' + hemispheres.charAt(normalizedDegrees < 0 ? 1 : 0));\n}\n/**\n * Transforms the given {@link module:ol/coordinate~Coordinate} to a string\n * using the given string template. The strings `{x}` and `{y}` in the template\n * will be replaced with the first and second coordinate values respectively.\n *\n * Example without specifying the fractional digits:\n *\n * import {format} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * var template = 'Coordinate is ({x}|{y}).';\n * var out = format(coord, template);\n * // out is now 'Coordinate is (8|48).'\n *\n * Example explicitly specifying the fractional digits:\n *\n * import {format} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * var template = 'Coordinate is ({x}|{y}).';\n * var out = format(coord, template, 2);\n * // out is now 'Coordinate is (7.85|47.98).'\n *\n * @param {Coordinate} coordinate Coordinate.\n * @param {string} template A template string with `{x}` and `{y}` placeholders\n * that will be replaced by first and second coordinate values.\n * @param {number=} opt_fractionDigits The number of digits to include\n * after the decimal point. Default is `0`.\n * @return {string} Formatted coordinate.\n * @api\n */\nexport function format(coordinate, template, opt_fractionDigits) {\n if (coordinate) {\n return template\n .replace('{x}', coordinate[0].toFixed(opt_fractionDigits))\n .replace('{y}', coordinate[1].toFixed(opt_fractionDigits));\n }\n else {\n return '';\n }\n}\n/**\n * @param {Coordinate} coordinate1 First coordinate.\n * @param {Coordinate} coordinate2 Second coordinate.\n * @return {boolean} The two coordinates are equal.\n */\nexport function equals(coordinate1, coordinate2) {\n var equals = true;\n for (var i = coordinate1.length - 1; i >= 0; --i) {\n if (coordinate1[i] != coordinate2[i]) {\n equals = false;\n break;\n }\n }\n return equals;\n}\n/**\n * Rotate `coordinate` by `angle`. `coordinate` is modified in place and\n * returned by the function.\n *\n * Example:\n *\n * import {rotate} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * var rotateRadians = Math.PI / 2; // 90 degrees\n * rotate(coord, rotateRadians);\n * // coord is now [-47.983333, 7.85]\n *\n * @param {Coordinate} coordinate Coordinate.\n * @param {number} angle Angle in radian.\n * @return {Coordinate} Coordinate.\n * @api\n */\nexport function rotate(coordinate, angle) {\n var cosAngle = Math.cos(angle);\n var sinAngle = Math.sin(angle);\n var x = coordinate[0] * cosAngle - coordinate[1] * sinAngle;\n var y = coordinate[1] * cosAngle + coordinate[0] * sinAngle;\n coordinate[0] = x;\n coordinate[1] = y;\n return coordinate;\n}\n/**\n * Scale `coordinate` by `scale`. `coordinate` is modified in place and returned\n * by the function.\n *\n * Example:\n *\n * import {scale as scaleCoordinate} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * var scale = 1.2;\n * scaleCoordinate(coord, scale);\n * // coord is now [9.42, 57.5799996]\n *\n * @param {Coordinate} coordinate Coordinate.\n * @param {number} scale Scale factor.\n * @return {Coordinate} Coordinate.\n */\nexport function scale(coordinate, scale) {\n coordinate[0] *= scale;\n coordinate[1] *= scale;\n return coordinate;\n}\n/**\n * @param {Coordinate} coord1 First coordinate.\n * @param {Coordinate} coord2 Second coordinate.\n * @return {number} Squared distance between coord1 and coord2.\n */\nexport function squaredDistance(coord1, coord2) {\n var dx = coord1[0] - coord2[0];\n var dy = coord1[1] - coord2[1];\n return dx * dx + dy * dy;\n}\n/**\n * @param {Coordinate} coord1 First coordinate.\n * @param {Coordinate} coord2 Second coordinate.\n * @return {number} Distance between coord1 and coord2.\n */\nexport function distance(coord1, coord2) {\n return Math.sqrt(squaredDistance(coord1, coord2));\n}\n/**\n * Calculate the squared distance from a coordinate to a line segment.\n *\n * @param {Coordinate} coordinate Coordinate of the point.\n * @param {Array} segment Line segment (2\n * coordinates).\n * @return {number} Squared distance from the point to the line segment.\n */\nexport function squaredDistanceToSegment(coordinate, segment) {\n return squaredDistance(coordinate, closestOnSegment(coordinate, segment));\n}\n/**\n * Format a geographic coordinate with the hemisphere, degrees, minutes, and\n * seconds.\n *\n * Example without specifying fractional digits:\n *\n * import {toStringHDMS} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * var out = toStringHDMS(coord);\n * // out is now '47° 58′ 60″ N 7° 50′ 60″ E'\n *\n * Example explicitly specifying 1 fractional digit:\n *\n * import {toStringHDMS} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * var out = toStringHDMS(coord, 1);\n * // out is now '47° 58′ 60.0″ N 7° 50′ 60.0″ E'\n *\n * @param {Coordinate} coordinate Coordinate.\n * @param {number=} opt_fractionDigits The number of digits to include\n * after the decimal point. Default is `0`.\n * @return {string} Hemisphere, degrees, minutes and seconds.\n * @api\n */\nexport function toStringHDMS(coordinate, opt_fractionDigits) {\n if (coordinate) {\n return degreesToStringHDMS('NS', coordinate[1], opt_fractionDigits) + ' ' +\n degreesToStringHDMS('EW', coordinate[0], opt_fractionDigits);\n }\n else {\n return '';\n }\n}\n/**\n * Format a coordinate as a comma delimited string.\n *\n * Example without specifying fractional digits:\n *\n * import {toStringXY} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * var out = toStringXY(coord);\n * // out is now '8, 48'\n *\n * Example explicitly specifying 1 fractional digit:\n *\n * import {toStringXY} from 'ol/coordinate';\n *\n * var coord = [7.85, 47.983333];\n * var out = toStringXY(coord, 1);\n * // out is now '7.8, 48.0'\n *\n * @param {Coordinate} coordinate Coordinate.\n * @param {number=} opt_fractionDigits The number of digits to include\n * after the decimal point. Default is `0`.\n * @return {string} XY.\n * @api\n */\nexport function toStringXY(coordinate, opt_fractionDigits) {\n return format(coordinate, '{x}, {y}', opt_fractionDigits);\n}\n//# sourceMappingURL=coordinate.js.map","/**\n * @module ol/easing\n */\n/**\n * Start slow and speed up.\n * @param {number} t Input between 0 and 1.\n * @return {number} Output between 0 and 1.\n * @api\n */\nexport function easeIn(t) {\n return Math.pow(t, 3);\n}\n/**\n * Start fast and slow down.\n * @param {number} t Input between 0 and 1.\n * @return {number} Output between 0 and 1.\n * @api\n */\nexport function easeOut(t) {\n return 1 - easeIn(1 - t);\n}\n/**\n * Start slow, speed up, and then slow down again.\n * @param {number} t Input between 0 and 1.\n * @return {number} Output between 0 and 1.\n * @api\n */\nexport function inAndOut(t) {\n return 3 * t * t - 2 * t * t * t;\n}\n/**\n * Maintain a constant speed over time.\n * @param {number} t Input between 0 and 1.\n * @return {number} Output between 0 and 1.\n * @api\n */\nexport function linear(t) {\n return t;\n}\n/**\n * Start slow, speed up, and at the very end slow down again. This has the\n * same general behavior as {@link module:ol/easing~inAndOut}, but the final\n * slowdown is delayed.\n * @param {number} t Input between 0 and 1.\n * @return {number} Output between 0 and 1.\n * @api\n */\nexport function upAndDown(t) {\n if (t < 0.5) {\n return inAndOut(2 * t);\n }\n else {\n return 1 - inAndOut(2 * (t - 0.5));\n }\n}\n//# sourceMappingURL=easing.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/View\n */\nimport { DEFAULT_TILE_SIZE } from './tilegrid/common.js';\nimport { getUid } from './util.js';\nimport { VOID } from './functions.js';\nimport { createExtent, none as centerNone } from './centerconstraint.js';\nimport BaseObject from './Object.js';\nimport { createSnapToResolutions, createSnapToPower } from './resolutionconstraint.js';\nimport { createSnapToZero, createSnapToN, none as rotationNone, disable } from './rotationconstraint.js';\nimport ViewHint from './ViewHint.js';\nimport ViewProperty from './ViewProperty.js';\nimport { linearFindNearest } from './array.js';\nimport { assert } from './asserts.js';\nimport { add as addCoordinate, rotate as rotateCoordinate, equals as coordinatesEqual } from './coordinate.js';\nimport { inAndOut } from './easing.js';\nimport { getForViewAndSize, getCenter, getHeight, getWidth, isEmpty } from './extent.js';\nimport GeometryType from './geom/GeometryType.js';\nimport { fromExtent as polygonFromExtent } from './geom/Polygon.js';\nimport { clamp, modulo } from './math.js';\nimport { assign } from './obj.js';\nimport { createProjection, METERS_PER_UNIT, toUserCoordinate, toUserExtent, fromUserCoordinate, fromUserExtent, getUserProjection } from './proj.js';\nimport Units from './proj/Units.js';\nimport { equals } from './coordinate.js';\nimport { easeOut } from './easing.js';\nimport { createMinMaxResolution } from './resolutionconstraint.js';\n/**\n * An animation configuration\n *\n * @typedef {Object} Animation\n * @property {import(\"./coordinate.js\").Coordinate} [sourceCenter]\n * @property {import(\"./coordinate.js\").Coordinate} [targetCenter]\n * @property {number} [sourceResolution]\n * @property {number} [targetResolution]\n * @property {number} [sourceRotation]\n * @property {number} [targetRotation]\n * @property {import(\"./coordinate.js\").Coordinate} [anchor]\n * @property {number} start\n * @property {number} duration\n * @property {boolean} complete\n * @property {function(number):number} easing\n * @property {function(boolean)} callback\n */\n/**\n * @typedef {Object} Constraints\n * @property {import(\"./centerconstraint.js\").Type} center\n * @property {import(\"./resolutionconstraint.js\").Type} resolution\n * @property {import(\"./rotationconstraint.js\").Type} rotation\n */\n/**\n * @typedef {Object} FitOptions\n * @property {import(\"./size.js\").Size} [size] The size in pixels of the box to fit\n * the extent into. Default is the current size of the first map in the DOM that\n * uses this view, or `[100, 100]` if no such map is found.\n * @property {!Array} [padding=[0, 0, 0, 0]] Padding (in pixels) to be\n * cleared inside the view. Values in the array are top, right, bottom and left\n * padding.\n * @property {boolean} [nearest=false] If the view `constrainResolution` option is `true`,\n * get the nearest extent instead of the closest that actually fits the view.\n * @property {number} [minResolution=0] Minimum resolution that we zoom to.\n * @property {number} [maxZoom] Maximum zoom level that we zoom to. If\n * `minResolution` is given, this property is ignored.\n * @property {number} [duration] The duration of the animation in milliseconds.\n * By default, there is no animation to the target extent.\n * @property {function(number):number} [easing] The easing function used during\n * the animation (defaults to {@link module:ol/easing~inAndOut}).\n * The function will be called for each frame with a number representing a\n * fraction of the animation's duration. The function should return a number\n * between 0 and 1 representing the progress toward the destination state.\n * @property {function(boolean)} [callback] Function called when the view is in\n * its final position. The callback will be called with `true` if the animation\n * series completed on its own or `false` if it was cancelled.\n */\n/**\n * @typedef {Object} ViewOptions\n * @property {import(\"./coordinate.js\").Coordinate} [center] The initial center for\n * the view. If a user projection is not set, the coordinate system for the center is\n * specified with the `projection` option. Layer sources will not be fetched if this\n * is not set, but the center can be set later with {@link #setCenter}.\n * @property {boolean|number} [constrainRotation=true] Rotation constraint.\n * `false` means no constraint. `true` means no constraint, but snap to zero\n * near zero. A number constrains the rotation to that number of values. For\n * example, `4` will constrain the rotation to 0, 90, 180, and 270 degrees.\n * @property {boolean} [enableRotation=true] Enable rotation.\n * If `false`, a rotation constraint that always sets the rotation to zero is\n * used. The `constrainRotation` option has no effect if `enableRotation` is\n * `false`.\n * @property {import(\"./extent.js\").Extent} [extent] The extent that constrains the\n * view, in other words, nothing outside of this extent can be visible on the map.\n * @property {boolean} [constrainOnlyCenter=false] If true, the extent\n * constraint will only apply to the view center and not the whole extent.\n * @property {boolean} [smoothExtentConstraint=true] If true, the extent\n * constraint will be applied smoothly, i.e. allow the view to go slightly outside\n * of the given `extent`.\n * @property {number} [maxResolution] The maximum resolution used to determine\n * the resolution constraint. It is used together with `minResolution` (or\n * `maxZoom`) and `zoomFactor`. If unspecified it is calculated in such a way\n * that the projection's validity extent fits in a 256x256 px tile. If the\n * projection is Spherical Mercator (the default) then `maxResolution` defaults\n * to `40075016.68557849 / 256 = 156543.03392804097`.\n * @property {number} [minResolution] The minimum resolution used to determine\n * the resolution constraint. It is used together with `maxResolution` (or\n * `minZoom`) and `zoomFactor`. If unspecified it is calculated assuming 29\n * zoom levels (with a factor of 2). If the projection is Spherical Mercator\n * (the default) then `minResolution` defaults to\n * `40075016.68557849 / 256 / Math.pow(2, 28) = 0.0005831682455839253`.\n * @property {number} [maxZoom=28] The maximum zoom level used to determine the\n * resolution constraint. It is used together with `minZoom` (or\n * `maxResolution`) and `zoomFactor`. Note that if `minResolution` is also\n * provided, it is given precedence over `maxZoom`.\n * @property {number} [minZoom=0] The minimum zoom level used to determine the\n * resolution constraint. It is used together with `maxZoom` (or\n * `minResolution`) and `zoomFactor`. Note that if `maxResolution` is also\n * provided, it is given precedence over `minZoom`.\n * @property {boolean} [multiWorld=false] If `false` the view is constrained so\n * only one world is visible, and you cannot pan off the edge. If `true` the map\n * may show multiple worlds at low zoom levels. Only used if the `projection` is\n * global. Note that if `extent` is also provided it is given precedence.\n * @property {boolean} [constrainResolution=false] If true, the view will always\n * animate to the closest zoom level after an interaction; false means\n * intermediary zoom levels are allowed.\n * @property {boolean} [smoothResolutionConstraint=true] If true, the resolution\n * min/max values will be applied smoothly, i. e. allow the view to exceed slightly\n * the given resolution or zoom bounds.\n * @property {import(\"./proj.js\").ProjectionLike} [projection='EPSG:3857'] The\n * projection. The default is Spherical Mercator.\n * @property {number} [resolution] The initial resolution for the view. The\n * units are `projection` units per pixel (e.g. meters per pixel). An\n * alternative to setting this is to set `zoom`. Layer sources will not be\n * fetched if neither this nor `zoom` are defined, but they can be set later\n * with {@link #setZoom} or {@link #setResolution}.\n * @property {Array} [resolutions] Resolutions to determine the\n * resolution constraint. If set the `maxResolution`, `minResolution`,\n * `minZoom`, `maxZoom`, and `zoomFactor` options are ignored.\n * @property {number} [rotation=0] The initial rotation for the view in radians\n * (positive rotation clockwise, 0 means North).\n * @property {number} [zoom] Only used if `resolution` is not defined. Zoom\n * level used to calculate the initial resolution for the view.\n * @property {number} [zoomFactor=2] The zoom factor used to compute the\n * corresponding resolution.\n */\n/**\n * @typedef {Object} AnimationOptions\n * @property {import(\"./coordinate.js\").Coordinate} [center] The center of the view at the end of\n * the animation.\n * @property {number} [zoom] The zoom level of the view at the end of the\n * animation. This takes precedence over `resolution`.\n * @property {number} [resolution] The resolution of the view at the end\n * of the animation. If `zoom` is also provided, this option will be ignored.\n * @property {number} [rotation] The rotation of the view at the end of\n * the animation.\n * @property {import(\"./coordinate.js\").Coordinate} [anchor] Optional anchor to remain fixed\n * during a rotation or resolution animation.\n * @property {number} [duration=1000] The duration of the animation in milliseconds.\n * @property {function(number):number} [easing] The easing function used\n * during the animation (defaults to {@link module:ol/easing~inAndOut}).\n * The function will be called for each frame with a number representing a\n * fraction of the animation's duration. The function should return a number\n * between 0 and 1 representing the progress toward the destination state.\n */\n/**\n * @typedef {Object} State\n * @property {import(\"./coordinate.js\").Coordinate} center\n * @property {import(\"./proj/Projection.js\").default} projection\n * @property {number} resolution\n * @property {number} rotation\n * @property {number} zoom\n */\n/**\n * Default min zoom level for the map view.\n * @type {number}\n */\nvar DEFAULT_MIN_ZOOM = 0;\n/**\n * @classdesc\n * A View object represents a simple 2D view of the map.\n *\n * This is the object to act upon to change the center, resolution,\n * and rotation of the map.\n *\n * A View has a `projection`. The projection determines the\n * coordinate system of the center, and its units determine the units of the\n * resolution (projection units per pixel). The default projection is\n * Spherical Mercator (EPSG:3857).\n *\n * ### The view states\n *\n * A View is determined by three states: `center`, `resolution`,\n * and `rotation`. Each state has a corresponding getter and setter, e.g.\n * `getCenter` and `setCenter` for the `center` state.\n *\n * The `zoom` state is actually not saved on the view: all computations\n * internally use the `resolution` state. Still, the `setZoom` and `getZoom`\n * methods are available, as well as `getResolutionForZoom` and\n * `getZoomForResolution` to switch from one system to the other.\n *\n * ### The constraints\n *\n * `setCenter`, `setResolution` and `setRotation` can be used to change the\n * states of the view, but any constraint defined in the constructor will\n * be applied along the way.\n *\n * A View object can have a *resolution constraint*, a *rotation constraint*\n * and a *center constraint*.\n *\n * The *resolution constraint* typically restricts min/max values and\n * snaps to specific resolutions. It is determined by the following\n * options: `resolutions`, `maxResolution`, `maxZoom` and `zoomFactor`.\n * If `resolutions` is set, the other three options are ignored. See\n * documentation for each option for more information. By default, the view\n * only has a min/max restriction and allow intermediary zoom levels when\n * pinch-zooming for example.\n *\n * The *rotation constraint* snaps to specific angles. It is determined\n * by the following options: `enableRotation` and `constrainRotation`.\n * By default rotation is allowed and its value is snapped to zero when approaching the\n * horizontal.\n *\n * The *center constraint* is determined by the `extent` option. By\n * default the view center is not constrained at all.\n *\n * ### Changing the view state\n *\n * It is important to note that `setZoom`, `setResolution`, `setCenter` and\n * `setRotation` are subject to the above mentioned constraints. As such, it\n * may sometimes not be possible to know in advance the resulting state of the\n * View. For example, calling `setResolution(10)` does not guarantee that\n * `getResolution()` will return `10`.\n *\n * A consequence of this is that, when applying a delta on the view state, one\n * should use `adjustCenter`, `adjustRotation`, `adjustZoom` and `adjustResolution`\n * rather than the corresponding setters. This will let view do its internal\n * computations. Besides, the `adjust*` methods also take an `opt_anchor`\n * argument which allows specifying an origin for the transformation.\n *\n * ### Interacting with the view\n *\n * View constraints are usually only applied when the view is *at rest*, meaning that\n * no interaction or animation is ongoing. As such, if the user puts the view in a\n * state that is not equivalent to a constrained one (e.g. rotating the view when\n * the snap angle is 0), an animation will be triggered at the interaction end to\n * put back the view to a stable state;\n *\n * @api\n */\nvar View = /** @class */ (function (_super) {\n __extends(View, _super);\n /**\n * @param {ViewOptions=} opt_options View options.\n */\n function View(opt_options) {\n var _this = _super.call(this) || this;\n var options = assign({}, opt_options);\n /**\n * @private\n * @type {Array}\n */\n _this.hints_ = [0, 0];\n /**\n * @private\n * @type {Array>}\n */\n _this.animations_ = [];\n /**\n * @private\n * @type {number|undefined}\n */\n _this.updateAnimationKey_;\n /**\n * @private\n * @const\n * @type {import(\"./proj/Projection.js\").default}\n */\n _this.projection_ = createProjection(options.projection, 'EPSG:3857');\n /**\n * @private\n * @type {import(\"./coordinate.js\").Coordinate|undefined}\n */\n _this.targetCenter_ = null;\n /**\n * @private\n * @type {number|undefined}\n */\n _this.targetResolution_;\n /**\n * @private\n * @type {number|undefined}\n */\n _this.targetRotation_;\n if (options.center) {\n options.center = fromUserCoordinate(options.center, _this.projection_);\n }\n if (options.extent) {\n options.extent = fromUserExtent(options.extent, _this.projection_);\n }\n _this.applyOptions_(options);\n return _this;\n }\n /**\n * Set up the view with the given options.\n * @param {ViewOptions} options View options.\n */\n View.prototype.applyOptions_ = function (options) {\n /**\n * @type {Object}\n */\n var properties = {};\n var resolutionConstraintInfo = createResolutionConstraint(options);\n /**\n * @private\n * @type {number}\n */\n this.maxResolution_ = resolutionConstraintInfo.maxResolution;\n /**\n * @private\n * @type {number}\n */\n this.minResolution_ = resolutionConstraintInfo.minResolution;\n /**\n * @private\n * @type {number}\n */\n this.zoomFactor_ = resolutionConstraintInfo.zoomFactor;\n /**\n * @private\n * @type {Array|undefined}\n */\n this.resolutions_ = options.resolutions;\n /**\n * @private\n * @type {number}\n */\n this.minZoom_ = resolutionConstraintInfo.minZoom;\n var centerConstraint = createCenterConstraint(options);\n var resolutionConstraint = resolutionConstraintInfo.constraint;\n var rotationConstraint = createRotationConstraint(options);\n /**\n * @private\n * @type {Constraints}\n */\n this.constraints_ = {\n center: centerConstraint,\n resolution: resolutionConstraint,\n rotation: rotationConstraint\n };\n this.setRotation(options.rotation !== undefined ? options.rotation : 0);\n this.setCenterInternal(options.center !== undefined ? options.center : null);\n if (options.resolution !== undefined) {\n this.setResolution(options.resolution);\n }\n else if (options.zoom !== undefined) {\n this.setZoom(options.zoom);\n }\n this.resolveConstraints(0);\n this.setProperties(properties);\n /**\n * @private\n * @type {ViewOptions}\n */\n this.options_ = options;\n };\n /**\n * Get an updated version of the view options used to construct the view. The\n * current resolution (or zoom), center, and rotation are applied to any stored\n * options. The provided options can be used to apply new min/max zoom or\n * resolution limits.\n * @param {ViewOptions} newOptions New options to be applied.\n * @return {ViewOptions} New options updated with the current view state.\n */\n View.prototype.getUpdatedOptions_ = function (newOptions) {\n var options = assign({}, this.options_);\n // preserve resolution (or zoom)\n if (options.resolution !== undefined) {\n options.resolution = this.getResolution();\n }\n else {\n options.zoom = this.getZoom();\n }\n // preserve center\n options.center = this.getCenterInternal();\n // preserve rotation\n options.rotation = this.getRotation();\n return assign({}, options, newOptions);\n };\n /**\n * Animate the view. The view's center, zoom (or resolution), and rotation\n * can be animated for smooth transitions between view states. For example,\n * to animate the view to a new zoom level:\n *\n * view.animate({zoom: view.getZoom() + 1});\n *\n * By default, the animation lasts one second and uses in-and-out easing. You\n * can customize this behavior by including `duration` (in milliseconds) and\n * `easing` options (see {@link module:ol/easing}).\n *\n * To chain together multiple animations, call the method with multiple\n * animation objects. For example, to first zoom and then pan:\n *\n * view.animate({zoom: 10}, {center: [0, 0]});\n *\n * If you provide a function as the last argument to the animate method, it\n * will get called at the end of an animation series. The callback will be\n * called with `true` if the animation series completed on its own or `false`\n * if it was cancelled.\n *\n * Animations are cancelled by user interactions (e.g. dragging the map) or by\n * calling `view.setCenter()`, `view.setResolution()`, or `view.setRotation()`\n * (or another method that calls one of these).\n *\n * @param {...(AnimationOptions|function(boolean): void)} var_args Animation\n * options. Multiple animations can be run in series by passing multiple\n * options objects. To run multiple animations in parallel, call the method\n * multiple times. An optional callback can be provided as a final\n * argument. The callback will be called with a boolean indicating whether\n * the animation completed without being cancelled.\n * @api\n */\n View.prototype.animate = function (var_args) {\n if (this.isDef() && !this.getAnimating()) {\n this.resolveConstraints(0);\n }\n var args = new Array(arguments.length);\n for (var i = 0; i < args.length; ++i) {\n var options = arguments[i];\n if (options.center) {\n options = assign({}, options);\n options.center = fromUserCoordinate(options.center, this.getProjection());\n }\n if (options.anchor) {\n options = assign({}, options);\n options.anchor = fromUserCoordinate(options.anchor, this.getProjection());\n }\n args[i] = options;\n }\n this.animateInternal.apply(this, args);\n };\n /**\n * @param {...(AnimationOptions|function(boolean): void)} var_args Animation options.\n */\n View.prototype.animateInternal = function (var_args) {\n var animationCount = arguments.length;\n var callback;\n if (animationCount > 1 && typeof arguments[animationCount - 1] === 'function') {\n callback = arguments[animationCount - 1];\n --animationCount;\n }\n if (!this.isDef()) {\n // if view properties are not yet set, shortcut to the final state\n var state = arguments[animationCount - 1];\n if (state.center) {\n this.setCenterInternal(state.center);\n }\n if (state.zoom !== undefined) {\n this.setZoom(state.zoom);\n }\n if (state.rotation !== undefined) {\n this.setRotation(state.rotation);\n }\n if (callback) {\n animationCallback(callback, true);\n }\n return;\n }\n var start = Date.now();\n var center = this.targetCenter_.slice();\n var resolution = this.targetResolution_;\n var rotation = this.targetRotation_;\n var series = [];\n for (var i = 0; i < animationCount; ++i) {\n var options = /** @type {AnimationOptions} */ (arguments[i]);\n var animation = {\n start: start,\n complete: false,\n anchor: options.anchor,\n duration: options.duration !== undefined ? options.duration : 1000,\n easing: options.easing || inAndOut,\n callback: callback\n };\n if (options.center) {\n animation.sourceCenter = center;\n animation.targetCenter = options.center.slice();\n center = animation.targetCenter;\n }\n if (options.zoom !== undefined) {\n animation.sourceResolution = resolution;\n animation.targetResolution = this.getResolutionForZoom(options.zoom);\n resolution = animation.targetResolution;\n }\n else if (options.resolution) {\n animation.sourceResolution = resolution;\n animation.targetResolution = options.resolution;\n resolution = animation.targetResolution;\n }\n if (options.rotation !== undefined) {\n animation.sourceRotation = rotation;\n var delta = modulo(options.rotation - rotation + Math.PI, 2 * Math.PI) - Math.PI;\n animation.targetRotation = rotation + delta;\n rotation = animation.targetRotation;\n }\n // check if animation is a no-op\n if (isNoopAnimation(animation)) {\n animation.complete = true;\n // we still push it onto the series for callback handling\n }\n else {\n start += animation.duration;\n }\n series.push(animation);\n }\n this.animations_.push(series);\n this.setHint(ViewHint.ANIMATING, 1);\n this.updateAnimations_();\n };\n /**\n * Determine if the view is being animated.\n * @return {boolean} The view is being animated.\n * @api\n */\n View.prototype.getAnimating = function () {\n return this.hints_[ViewHint.ANIMATING] > 0;\n };\n /**\n * Determine if the user is interacting with the view, such as panning or zooming.\n * @return {boolean} The view is being interacted with.\n * @api\n */\n View.prototype.getInteracting = function () {\n return this.hints_[ViewHint.INTERACTING] > 0;\n };\n /**\n * Cancel any ongoing animations.\n * @api\n */\n View.prototype.cancelAnimations = function () {\n this.setHint(ViewHint.ANIMATING, -this.hints_[ViewHint.ANIMATING]);\n for (var i = 0, ii = this.animations_.length; i < ii; ++i) {\n var series = this.animations_[i];\n if (series[0].callback) {\n animationCallback(series[0].callback, false);\n }\n }\n this.animations_.length = 0;\n };\n /**\n * Update all animations.\n */\n View.prototype.updateAnimations_ = function () {\n if (this.updateAnimationKey_ !== undefined) {\n cancelAnimationFrame(this.updateAnimationKey_);\n this.updateAnimationKey_ = undefined;\n }\n if (!this.getAnimating()) {\n return;\n }\n var now = Date.now();\n var more = false;\n for (var i = this.animations_.length - 1; i >= 0; --i) {\n var series = this.animations_[i];\n var seriesComplete = true;\n for (var j = 0, jj = series.length; j < jj; ++j) {\n var animation = series[j];\n if (animation.complete) {\n continue;\n }\n var elapsed = now - animation.start;\n var fraction = animation.duration > 0 ? elapsed / animation.duration : 1;\n if (fraction >= 1) {\n animation.complete = true;\n fraction = 1;\n }\n else {\n seriesComplete = false;\n }\n var progress = animation.easing(fraction);\n if (animation.sourceCenter) {\n var x0 = animation.sourceCenter[0];\n var y0 = animation.sourceCenter[1];\n var x1 = animation.targetCenter[0];\n var y1 = animation.targetCenter[1];\n var x = x0 + progress * (x1 - x0);\n var y = y0 + progress * (y1 - y0);\n this.targetCenter_ = [x, y];\n }\n if (animation.sourceResolution && animation.targetResolution) {\n var resolution = progress === 1 ?\n animation.targetResolution :\n animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution);\n if (animation.anchor) {\n var size = this.getSizeFromViewport_(this.getRotation());\n var constrainedResolution = this.constraints_.resolution(resolution, 0, size, true);\n this.targetCenter_ = this.calculateCenterZoom(constrainedResolution, animation.anchor);\n }\n this.targetResolution_ = resolution;\n this.applyTargetState_(true);\n }\n if (animation.sourceRotation !== undefined && animation.targetRotation !== undefined) {\n var rotation = progress === 1 ?\n modulo(animation.targetRotation + Math.PI, 2 * Math.PI) - Math.PI :\n animation.sourceRotation + progress * (animation.targetRotation - animation.sourceRotation);\n if (animation.anchor) {\n var constrainedRotation = this.constraints_.rotation(rotation, true);\n this.targetCenter_ = this.calculateCenterRotate(constrainedRotation, animation.anchor);\n }\n this.targetRotation_ = rotation;\n }\n this.applyTargetState_(true);\n more = true;\n if (!animation.complete) {\n break;\n }\n }\n if (seriesComplete) {\n this.animations_[i] = null;\n this.setHint(ViewHint.ANIMATING, -1);\n var callback = series[0].callback;\n if (callback) {\n animationCallback(callback, true);\n }\n }\n }\n // prune completed series\n this.animations_ = this.animations_.filter(Boolean);\n if (more && this.updateAnimationKey_ === undefined) {\n this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_.bind(this));\n }\n };\n /**\n * @param {number} rotation Target rotation.\n * @param {import(\"./coordinate.js\").Coordinate} anchor Rotation anchor.\n * @return {import(\"./coordinate.js\").Coordinate|undefined} Center for rotation and anchor.\n */\n View.prototype.calculateCenterRotate = function (rotation, anchor) {\n var center;\n var currentCenter = this.getCenterInternal();\n if (currentCenter !== undefined) {\n center = [currentCenter[0] - anchor[0], currentCenter[1] - anchor[1]];\n rotateCoordinate(center, rotation - this.getRotation());\n addCoordinate(center, anchor);\n }\n return center;\n };\n /**\n * @param {number} resolution Target resolution.\n * @param {import(\"./coordinate.js\").Coordinate} anchor Zoom anchor.\n * @return {import(\"./coordinate.js\").Coordinate|undefined} Center for resolution and anchor.\n */\n View.prototype.calculateCenterZoom = function (resolution, anchor) {\n var center;\n var currentCenter = this.getCenterInternal();\n var currentResolution = this.getResolution();\n if (currentCenter !== undefined && currentResolution !== undefined) {\n var x = anchor[0] - resolution * (anchor[0] - currentCenter[0]) / currentResolution;\n var y = anchor[1] - resolution * (anchor[1] - currentCenter[1]) / currentResolution;\n center = [x, y];\n }\n return center;\n };\n /**\n * @private\n * @param {number=} opt_rotation Take into account the rotation of the viewport when giving the size\n * @return {import(\"./size.js\").Size} Viewport size or `[100, 100]` when no viewport is found.\n */\n View.prototype.getSizeFromViewport_ = function (opt_rotation) {\n var size = [100, 100];\n var selector = '.ol-viewport[data-view=\"' + getUid(this) + '\"]';\n var element = document.querySelector(selector);\n if (element) {\n var metrics = getComputedStyle(element);\n size[0] = parseInt(metrics.width, 10);\n size[1] = parseInt(metrics.height, 10);\n }\n if (opt_rotation) {\n var w = size[0];\n var h = size[1];\n size[0] = Math.abs(w * Math.cos(opt_rotation)) + Math.abs(h * Math.sin(opt_rotation));\n size[1] = Math.abs(w * Math.sin(opt_rotation)) + Math.abs(h * Math.cos(opt_rotation));\n }\n return size;\n };\n /**\n * Get the view center.\n * @return {import(\"./coordinate.js\").Coordinate|undefined} The center of the view.\n * @observable\n * @api\n */\n View.prototype.getCenter = function () {\n var center = this.getCenterInternal();\n if (!center) {\n return center;\n }\n return toUserCoordinate(center, this.getProjection());\n };\n /**\n * Get the view center without transforming to user projection.\n * @return {import(\"./coordinate.js\").Coordinate|undefined} The center of the view.\n */\n View.prototype.getCenterInternal = function () {\n return /** @type {import(\"./coordinate.js\").Coordinate|undefined} */ (this.get(ViewProperty.CENTER));\n };\n /**\n * @return {Constraints} Constraints.\n */\n View.prototype.getConstraints = function () {\n return this.constraints_;\n };\n /**\n * @param {Array=} opt_hints Destination array.\n * @return {Array} Hint.\n */\n View.prototype.getHints = function (opt_hints) {\n if (opt_hints !== undefined) {\n opt_hints[0] = this.hints_[0];\n opt_hints[1] = this.hints_[1];\n return opt_hints;\n }\n else {\n return this.hints_.slice();\n }\n };\n /**\n * Calculate the extent for the current view state and the passed size.\n * The size is the pixel dimensions of the box into which the calculated extent\n * should fit. In most cases you want to get the extent of the entire map,\n * that is `map.getSize()`.\n * @param {import(\"./size.js\").Size=} opt_size Box pixel size. If not provided, the size of the\n * first map that uses this view will be used.\n * @return {import(\"./extent.js\").Extent} Extent.\n * @api\n */\n View.prototype.calculateExtent = function (opt_size) {\n var extent = this.calculateExtentInternal(opt_size);\n return toUserExtent(extent, this.getProjection());\n };\n /**\n * @param {import(\"./size.js\").Size=} opt_size Box pixel size. If not provided, the size of the\n * first map that uses this view will be used.\n * @return {import(\"./extent.js\").Extent} Extent.\n */\n View.prototype.calculateExtentInternal = function (opt_size) {\n var size = opt_size || this.getSizeFromViewport_();\n var center = /** @type {!import(\"./coordinate.js\").Coordinate} */ (this.getCenterInternal());\n assert(center, 1); // The view center is not defined\n var resolution = /** @type {!number} */ (this.getResolution());\n assert(resolution !== undefined, 2); // The view resolution is not defined\n var rotation = /** @type {!number} */ (this.getRotation());\n assert(rotation !== undefined, 3); // The view rotation is not defined\n return getForViewAndSize(center, resolution, rotation, size);\n };\n /**\n * Get the maximum resolution of the view.\n * @return {number} The maximum resolution of the view.\n * @api\n */\n View.prototype.getMaxResolution = function () {\n return this.maxResolution_;\n };\n /**\n * Get the minimum resolution of the view.\n * @return {number} The minimum resolution of the view.\n * @api\n */\n View.prototype.getMinResolution = function () {\n return this.minResolution_;\n };\n /**\n * Get the maximum zoom level for the view.\n * @return {number} The maximum zoom level.\n * @api\n */\n View.prototype.getMaxZoom = function () {\n return /** @type {number} */ (this.getZoomForResolution(this.minResolution_));\n };\n /**\n * Set a new maximum zoom level for the view.\n * @param {number} zoom The maximum zoom level.\n * @api\n */\n View.prototype.setMaxZoom = function (zoom) {\n this.applyOptions_(this.getUpdatedOptions_({ maxZoom: zoom }));\n };\n /**\n * Get the minimum zoom level for the view.\n * @return {number} The minimum zoom level.\n * @api\n */\n View.prototype.getMinZoom = function () {\n return /** @type {number} */ (this.getZoomForResolution(this.maxResolution_));\n };\n /**\n * Set a new minimum zoom level for the view.\n * @param {number} zoom The minimum zoom level.\n * @api\n */\n View.prototype.setMinZoom = function (zoom) {\n this.applyOptions_(this.getUpdatedOptions_({ minZoom: zoom }));\n };\n /**\n * Set whether the view shoud allow intermediary zoom levels.\n * @param {boolean} enabled Whether the resolution is constrained.\n * @api\n */\n View.prototype.setConstrainResolution = function (enabled) {\n this.applyOptions_(this.getUpdatedOptions_({ constrainResolution: enabled }));\n };\n /**\n * Get the view projection.\n * @return {import(\"./proj/Projection.js\").default} The projection of the view.\n * @api\n */\n View.prototype.getProjection = function () {\n return this.projection_;\n };\n /**\n * Get the view resolution.\n * @return {number|undefined} The resolution of the view.\n * @observable\n * @api\n */\n View.prototype.getResolution = function () {\n return /** @type {number|undefined} */ (this.get(ViewProperty.RESOLUTION));\n };\n /**\n * Get the resolutions for the view. This returns the array of resolutions\n * passed to the constructor of the View, or undefined if none were given.\n * @return {Array|undefined} The resolutions of the view.\n * @api\n */\n View.prototype.getResolutions = function () {\n return this.resolutions_;\n };\n /**\n * Get the resolution for a provided extent (in map units) and size (in pixels).\n * @param {import(\"./extent.js\").Extent} extent Extent.\n * @param {import(\"./size.js\").Size=} opt_size Box pixel size.\n * @return {number} The resolution at which the provided extent will render at\n * the given size.\n * @api\n */\n View.prototype.getResolutionForExtent = function (extent, opt_size) {\n return this.getResolutionForExtentInternal(fromUserExtent(extent, this.getProjection()), opt_size);\n };\n /**\n * Get the resolution for a provided extent (in map units) and size (in pixels).\n * @param {import(\"./extent.js\").Extent} extent Extent.\n * @param {import(\"./size.js\").Size=} opt_size Box pixel size.\n * @return {number} The resolution at which the provided extent will render at\n * the given size.\n */\n View.prototype.getResolutionForExtentInternal = function (extent, opt_size) {\n var size = opt_size || this.getSizeFromViewport_();\n var xResolution = getWidth(extent) / size[0];\n var yResolution = getHeight(extent) / size[1];\n return Math.max(xResolution, yResolution);\n };\n /**\n * Return a function that returns a value between 0 and 1 for a\n * resolution. Exponential scaling is assumed.\n * @param {number=} opt_power Power.\n * @return {function(number): number} Resolution for value function.\n */\n View.prototype.getResolutionForValueFunction = function (opt_power) {\n var power = opt_power || 2;\n var maxResolution = this.maxResolution_;\n var minResolution = this.minResolution_;\n var max = Math.log(maxResolution / minResolution) / Math.log(power);\n return (\n /**\n * @param {number} value Value.\n * @return {number} Resolution.\n */\n function (value) {\n var resolution = maxResolution / Math.pow(power, value * max);\n return resolution;\n });\n };\n /**\n * Get the view rotation.\n * @return {number} The rotation of the view in radians.\n * @observable\n * @api\n */\n View.prototype.getRotation = function () {\n return /** @type {number} */ (this.get(ViewProperty.ROTATION));\n };\n /**\n * Return a function that returns a resolution for a value between\n * 0 and 1. Exponential scaling is assumed.\n * @param {number=} opt_power Power.\n * @return {function(number): number} Value for resolution function.\n */\n View.prototype.getValueForResolutionFunction = function (opt_power) {\n var power = opt_power || 2;\n var maxResolution = this.maxResolution_;\n var minResolution = this.minResolution_;\n var max = Math.log(maxResolution / minResolution) / Math.log(power);\n return (\n /**\n * @param {number} resolution Resolution.\n * @return {number} Value.\n */\n function (resolution) {\n var value = (Math.log(maxResolution / resolution) / Math.log(power)) / max;\n return value;\n });\n };\n /**\n * @return {State} View state.\n */\n View.prototype.getState = function () {\n var center = /** @type {import(\"./coordinate.js\").Coordinate} */ (this.getCenterInternal());\n var projection = this.getProjection();\n var resolution = /** @type {number} */ (this.getResolution());\n var rotation = this.getRotation();\n return {\n center: center.slice(0),\n projection: projection !== undefined ? projection : null,\n resolution: resolution,\n rotation: rotation,\n zoom: this.getZoom()\n };\n };\n /**\n * Get the current zoom level. This method may return non-integer zoom levels\n * if the view does not constrain the resolution, or if an interaction or\n * animation is underway.\n * @return {number|undefined} Zoom.\n * @api\n */\n View.prototype.getZoom = function () {\n var zoom;\n var resolution = this.getResolution();\n if (resolution !== undefined) {\n zoom = this.getZoomForResolution(resolution);\n }\n return zoom;\n };\n /**\n * Get the zoom level for a resolution.\n * @param {number} resolution The resolution.\n * @return {number|undefined} The zoom level for the provided resolution.\n * @api\n */\n View.prototype.getZoomForResolution = function (resolution) {\n var offset = this.minZoom_ || 0;\n var max, zoomFactor;\n if (this.resolutions_) {\n var nearest = linearFindNearest(this.resolutions_, resolution, 1);\n offset = nearest;\n max = this.resolutions_[nearest];\n if (nearest == this.resolutions_.length - 1) {\n zoomFactor = 2;\n }\n else {\n zoomFactor = max / this.resolutions_[nearest + 1];\n }\n }\n else {\n max = this.maxResolution_;\n zoomFactor = this.zoomFactor_;\n }\n return offset + Math.log(max / resolution) / Math.log(zoomFactor);\n };\n /**\n * Get the resolution for a zoom level.\n * @param {number} zoom Zoom level.\n * @return {number} The view resolution for the provided zoom level.\n * @api\n */\n View.prototype.getResolutionForZoom = function (zoom) {\n if (this.resolutions_) {\n if (this.resolutions_.length <= 1) {\n return 0;\n }\n var baseLevel = clamp(Math.floor(zoom), 0, this.resolutions_.length - 2);\n var zoomFactor = this.resolutions_[baseLevel] / this.resolutions_[baseLevel + 1];\n return this.resolutions_[baseLevel] / Math.pow(zoomFactor, clamp(zoom - baseLevel, 0, 1));\n }\n else {\n return this.maxResolution_ / Math.pow(this.zoomFactor_, zoom - this.minZoom_);\n }\n };\n /**\n * Fit the given geometry or extent based on the given map size and border.\n * The size is pixel dimensions of the box to fit the extent into.\n * In most cases you will want to use the map size, that is `map.getSize()`.\n * Takes care of the map angle.\n * @param {import(\"./geom/SimpleGeometry.js\").default|import(\"./extent.js\").Extent} geometryOrExtent The geometry or\n * extent to fit the view to.\n * @param {FitOptions=} opt_options Options.\n * @api\n */\n View.prototype.fit = function (geometryOrExtent, opt_options) {\n var options = assign({ size: this.getSizeFromViewport_() }, opt_options || {});\n /** @type {import(\"./geom/SimpleGeometry.js\").default} */\n var geometry;\n assert(Array.isArray(geometryOrExtent) || typeof /** @type {?} */ (geometryOrExtent).getSimplifiedGeometry === 'function', 24); // Invalid extent or geometry provided as `geometry`\n if (Array.isArray(geometryOrExtent)) {\n assert(!isEmpty(geometryOrExtent), 25); // Cannot fit empty extent provided as `geometry`\n var extent = fromUserExtent(geometryOrExtent, this.getProjection());\n geometry = polygonFromExtent(extent);\n }\n else if (geometryOrExtent.getType() === GeometryType.CIRCLE) {\n var extent = fromUserExtent(geometryOrExtent.getExtent(), this.getProjection());\n geometry = polygonFromExtent(extent);\n geometry.rotate(this.getRotation(), getCenter(extent));\n }\n else {\n var userProjection = getUserProjection();\n if (userProjection) {\n geometry = /** @type {import(\"./geom/SimpleGeometry.js\").default} */ (geometry.clone().transform(userProjection, this.getProjection()));\n }\n else {\n geometry = geometryOrExtent;\n }\n }\n this.fitInternal(geometry, options);\n };\n /**\n * @param {import(\"./geom/SimpleGeometry.js\").default} geometry The geometry.\n * @param {FitOptions=} opt_options Options.\n */\n View.prototype.fitInternal = function (geometry, opt_options) {\n var options = opt_options || {};\n var size = options.size;\n if (!size) {\n size = this.getSizeFromViewport_();\n }\n var padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0];\n var nearest = options.nearest !== undefined ? options.nearest : false;\n var minResolution;\n if (options.minResolution !== undefined) {\n minResolution = options.minResolution;\n }\n else if (options.maxZoom !== undefined) {\n minResolution = this.getResolutionForZoom(options.maxZoom);\n }\n else {\n minResolution = 0;\n }\n var coords = geometry.getFlatCoordinates();\n // calculate rotated extent\n var rotation = this.getRotation();\n var cosAngle = Math.cos(-rotation);\n var sinAngle = Math.sin(-rotation);\n var minRotX = +Infinity;\n var minRotY = +Infinity;\n var maxRotX = -Infinity;\n var maxRotY = -Infinity;\n var stride = geometry.getStride();\n for (var i = 0, ii = coords.length; i < ii; i += stride) {\n var rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle;\n var rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle;\n minRotX = Math.min(minRotX, rotX);\n minRotY = Math.min(minRotY, rotY);\n maxRotX = Math.max(maxRotX, rotX);\n maxRotY = Math.max(maxRotY, rotY);\n }\n // calculate resolution\n var resolution = this.getResolutionForExtentInternal([minRotX, minRotY, maxRotX, maxRotY], [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]);\n resolution = isNaN(resolution) ? minResolution :\n Math.max(resolution, minResolution);\n resolution = this.getConstrainedResolution(resolution, nearest ? 0 : 1);\n // calculate center\n sinAngle = -sinAngle; // go back to original rotation\n var centerRotX = (minRotX + maxRotX) / 2;\n var centerRotY = (minRotY + maxRotY) / 2;\n centerRotX += (padding[1] - padding[3]) / 2 * resolution;\n centerRotY += (padding[0] - padding[2]) / 2 * resolution;\n var centerX = centerRotX * cosAngle - centerRotY * sinAngle;\n var centerY = centerRotY * cosAngle + centerRotX * sinAngle;\n var center = [centerX, centerY];\n var callback = options.callback ? options.callback : VOID;\n if (options.duration !== undefined) {\n this.animateInternal({\n resolution: resolution,\n center: this.getConstrainedCenter(center, resolution),\n duration: options.duration,\n easing: options.easing\n }, callback);\n }\n else {\n this.targetResolution_ = resolution;\n this.targetCenter_ = center;\n this.applyTargetState_(false, true);\n animationCallback(callback, true);\n }\n };\n /**\n * Center on coordinate and view position.\n * @param {import(\"./coordinate.js\").Coordinate} coordinate Coordinate.\n * @param {import(\"./size.js\").Size} size Box pixel size.\n * @param {import(\"./pixel.js\").Pixel} position Position on the view to center on.\n * @api\n */\n View.prototype.centerOn = function (coordinate, size, position) {\n this.centerOnInternal(fromUserCoordinate(coordinate, this.getProjection()), size, position);\n };\n /**\n * @param {import(\"./coordinate.js\").Coordinate} coordinate Coordinate.\n * @param {import(\"./size.js\").Size} size Box pixel size.\n * @param {import(\"./pixel.js\").Pixel} position Position on the view to center on.\n */\n View.prototype.centerOnInternal = function (coordinate, size, position) {\n // calculate rotated position\n var rotation = this.getRotation();\n var cosAngle = Math.cos(-rotation);\n var sinAngle = Math.sin(-rotation);\n var rotX = coordinate[0] * cosAngle - coordinate[1] * sinAngle;\n var rotY = coordinate[1] * cosAngle + coordinate[0] * sinAngle;\n var resolution = this.getResolution();\n rotX += (size[0] / 2 - position[0]) * resolution;\n rotY += (position[1] - size[1] / 2) * resolution;\n // go back to original angle\n sinAngle = -sinAngle; // go back to original rotation\n var centerX = rotX * cosAngle - rotY * sinAngle;\n var centerY = rotY * cosAngle + rotX * sinAngle;\n this.setCenterInternal([centerX, centerY]);\n };\n /**\n * @return {boolean} Is defined.\n */\n View.prototype.isDef = function () {\n return !!this.getCenterInternal() && this.getResolution() !== undefined;\n };\n /**\n * Adds relative coordinates to the center of the view. Any extent constraint will apply.\n * @param {import(\"./coordinate.js\").Coordinate} deltaCoordinates Relative value to add.\n * @api\n */\n View.prototype.adjustCenter = function (deltaCoordinates) {\n var center = toUserCoordinate(this.targetCenter_, this.getProjection());\n this.setCenter([center[0] + deltaCoordinates[0], center[1] + deltaCoordinates[1]]);\n };\n /**\n * Adds relative coordinates to the center of the view. Any extent constraint will apply.\n * @param {import(\"./coordinate.js\").Coordinate} deltaCoordinates Relative value to add.\n */\n View.prototype.adjustCenterInternal = function (deltaCoordinates) {\n var center = this.targetCenter_;\n this.setCenterInternal([center[0] + deltaCoordinates[0], center[1] + deltaCoordinates[1]]);\n };\n /**\n * Multiply the view resolution by a ratio, optionally using an anchor. Any resolution\n * constraint will apply.\n * @param {number} ratio The ratio to apply on the view resolution.\n * @param {import(\"./coordinate.js\").Coordinate=} opt_anchor The origin of the transformation.\n * @api\n */\n View.prototype.adjustResolution = function (ratio, opt_anchor) {\n var anchor = opt_anchor && fromUserCoordinate(opt_anchor, this.getProjection());\n this.adjustResolutionInternal(ratio, anchor);\n };\n /**\n * Multiply the view resolution by a ratio, optionally using an anchor. Any resolution\n * constraint will apply.\n * @param {number} ratio The ratio to apply on the view resolution.\n * @param {import(\"./coordinate.js\").Coordinate=} opt_anchor The origin of the transformation.\n */\n View.prototype.adjustResolutionInternal = function (ratio, opt_anchor) {\n var isMoving = this.getAnimating() || this.getInteracting();\n var size = this.getSizeFromViewport_(this.getRotation());\n var newResolution = this.constraints_.resolution(this.targetResolution_ * ratio, 0, size, isMoving);\n if (opt_anchor !== undefined) {\n this.targetCenter_ = this.calculateCenterZoom(newResolution, opt_anchor);\n }\n this.targetResolution_ *= ratio;\n this.applyTargetState_();\n };\n /**\n * Adds a value to the view zoom level, optionally using an anchor. Any resolution\n * constraint will apply.\n * @param {number} delta Relative value to add to the zoom level.\n * @param {import(\"./coordinate.js\").Coordinate=} opt_anchor The origin of the transformation.\n * @api\n */\n View.prototype.adjustZoom = function (delta, opt_anchor) {\n this.adjustResolution(Math.pow(this.zoomFactor_, -delta), opt_anchor);\n };\n /**\n * Adds a value to the view rotation, optionally using an anchor. Any rotation\n * constraint will apply.\n * @param {number} delta Relative value to add to the zoom rotation, in radians.\n * @param {import(\"./coordinate.js\").Coordinate=} opt_anchor The rotation center.\n * @api\n */\n View.prototype.adjustRotation = function (delta, opt_anchor) {\n if (opt_anchor) {\n opt_anchor = fromUserCoordinate(opt_anchor, this.getProjection());\n }\n this.adjustRotationInternal(delta, opt_anchor);\n };\n /**\n * @param {number} delta Relative value to add to the zoom rotation, in radians.\n * @param {import(\"./coordinate.js\").Coordinate=} opt_anchor The rotation center.\n */\n View.prototype.adjustRotationInternal = function (delta, opt_anchor) {\n var isMoving = this.getAnimating() || this.getInteracting();\n var newRotation = this.constraints_.rotation(this.targetRotation_ + delta, isMoving);\n if (opt_anchor !== undefined) {\n this.targetCenter_ = this.calculateCenterRotate(newRotation, opt_anchor);\n }\n this.targetRotation_ += delta;\n this.applyTargetState_();\n };\n /**\n * Set the center of the current view. Any extent constraint will apply.\n * @param {import(\"./coordinate.js\").Coordinate|undefined} center The center of the view.\n * @observable\n * @api\n */\n View.prototype.setCenter = function (center) {\n this.setCenterInternal(fromUserCoordinate(center, this.getProjection()));\n };\n /**\n * Set the center using the view projection (not the user projection).\n * @param {import(\"./coordinate.js\").Coordinate|undefined} center The center of the view.\n */\n View.prototype.setCenterInternal = function (center) {\n this.targetCenter_ = center;\n this.applyTargetState_();\n };\n /**\n * @param {ViewHint} hint Hint.\n * @param {number} delta Delta.\n * @return {number} New value.\n */\n View.prototype.setHint = function (hint, delta) {\n this.hints_[hint] += delta;\n this.changed();\n return this.hints_[hint];\n };\n /**\n * Set the resolution for this view. Any resolution constraint will apply.\n * @param {number|undefined} resolution The resolution of the view.\n * @observable\n * @api\n */\n View.prototype.setResolution = function (resolution) {\n this.targetResolution_ = resolution;\n this.applyTargetState_();\n };\n /**\n * Set the rotation for this view. Any rotation constraint will apply.\n * @param {number} rotation The rotation of the view in radians.\n * @observable\n * @api\n */\n View.prototype.setRotation = function (rotation) {\n this.targetRotation_ = rotation;\n this.applyTargetState_();\n };\n /**\n * Zoom to a specific zoom level. Any resolution constrain will apply.\n * @param {number} zoom Zoom level.\n * @api\n */\n View.prototype.setZoom = function (zoom) {\n this.setResolution(this.getResolutionForZoom(zoom));\n };\n /**\n * Recompute rotation/resolution/center based on target values.\n * Note: we have to compute rotation first, then resolution and center considering that\n * parameters can influence one another in case a view extent constraint is present.\n * @param {boolean=} opt_doNotCancelAnims Do not cancel animations.\n * @param {boolean=} opt_forceMoving Apply constraints as if the view is moving.\n * @private\n */\n View.prototype.applyTargetState_ = function (opt_doNotCancelAnims, opt_forceMoving) {\n var isMoving = this.getAnimating() || this.getInteracting() || opt_forceMoving;\n // compute rotation\n var newRotation = this.constraints_.rotation(this.targetRotation_, isMoving);\n var size = this.getSizeFromViewport_(newRotation);\n var newResolution = this.constraints_.resolution(this.targetResolution_, 0, size, isMoving);\n var newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, isMoving);\n if (this.get(ViewProperty.ROTATION) !== newRotation) {\n this.set(ViewProperty.ROTATION, newRotation);\n }\n if (this.get(ViewProperty.RESOLUTION) !== newResolution) {\n this.set(ViewProperty.RESOLUTION, newResolution);\n }\n if (!this.get(ViewProperty.CENTER) || !equals(this.get(ViewProperty.CENTER), newCenter)) {\n this.set(ViewProperty.CENTER, newCenter);\n }\n if (this.getAnimating() && !opt_doNotCancelAnims) {\n this.cancelAnimations();\n }\n };\n /**\n * If any constraints need to be applied, an animation will be triggered.\n * This is typically done on interaction end.\n * Note: calling this with a duration of 0 will apply the constrained values straight away,\n * without animation.\n * @param {number=} opt_duration The animation duration in ms.\n * @param {number=} opt_resolutionDirection Which direction to zoom.\n * @param {import(\"./coordinate.js\").Coordinate=} opt_anchor The origin of the transformation.\n */\n View.prototype.resolveConstraints = function (opt_duration, opt_resolutionDirection, opt_anchor) {\n var duration = opt_duration !== undefined ? opt_duration : 200;\n var direction = opt_resolutionDirection || 0;\n var newRotation = this.constraints_.rotation(this.targetRotation_);\n var size = this.getSizeFromViewport_(newRotation);\n var newResolution = this.constraints_.resolution(this.targetResolution_, direction, size);\n var newCenter = this.constraints_.center(this.targetCenter_, newResolution, size);\n if (duration === 0) {\n this.targetResolution_ = newResolution;\n this.targetRotation_ = newRotation;\n this.targetCenter_ = newCenter;\n this.applyTargetState_();\n return;\n }\n if (this.getResolution() !== newResolution ||\n this.getRotation() !== newRotation ||\n !this.getCenterInternal() ||\n !equals(this.getCenterInternal(), newCenter)) {\n if (this.getAnimating()) {\n this.cancelAnimations();\n }\n this.animateInternal({\n rotation: newRotation,\n center: newCenter,\n resolution: newResolution,\n duration: duration,\n easing: easeOut,\n anchor: opt_anchor\n });\n }\n };\n /**\n * Notify the View that an interaction has started.\n * The view state will be resolved to a stable one if needed\n * (depending on its constraints).\n * @api\n */\n View.prototype.beginInteraction = function () {\n this.resolveConstraints(0);\n this.setHint(ViewHint.INTERACTING, 1);\n };\n /**\n * Notify the View that an interaction has ended. The view state will be resolved\n * to a stable one if needed (depending on its constraints).\n * @param {number=} opt_duration Animation duration in ms.\n * @param {number=} opt_resolutionDirection Which direction to zoom.\n * @param {import(\"./coordinate.js\").Coordinate=} opt_anchor The origin of the transformation.\n * @api\n */\n View.prototype.endInteraction = function (opt_duration, opt_resolutionDirection, opt_anchor) {\n var anchor = opt_anchor && fromUserCoordinate(opt_anchor, this.getProjection());\n this.endInteractionInternal(opt_duration, opt_resolutionDirection, anchor);\n };\n /**\n * Notify the View that an interaction has ended. The view state will be resolved\n * to a stable one if needed (depending on its constraints).\n * @param {number=} opt_duration Animation duration in ms.\n * @param {number=} opt_resolutionDirection Which direction to zoom.\n * @param {import(\"./coordinate.js\").Coordinate=} opt_anchor The origin of the transformation.\n */\n View.prototype.endInteractionInternal = function (opt_duration, opt_resolutionDirection, opt_anchor) {\n this.setHint(ViewHint.INTERACTING, -1);\n this.resolveConstraints(opt_duration, opt_resolutionDirection, opt_anchor);\n };\n /**\n * Get a valid position for the view center according to the current constraints.\n * @param {import(\"./coordinate.js\").Coordinate|undefined} targetCenter Target center position.\n * @param {number=} opt_targetResolution Target resolution. If not supplied, the current one will be used.\n * This is useful to guess a valid center position at a different zoom level.\n * @return {import(\"./coordinate.js\").Coordinate|undefined} Valid center position.\n */\n View.prototype.getConstrainedCenter = function (targetCenter, opt_targetResolution) {\n var size = this.getSizeFromViewport_(this.getRotation());\n return this.constraints_.center(targetCenter, opt_targetResolution || this.getResolution(), size);\n };\n /**\n * Get a valid zoom level according to the current view constraints.\n * @param {number|undefined} targetZoom Target zoom.\n * @param {number=} [opt_direction=0] Indicate which resolution should be used\n * by a renderer if the view resolution does not match any resolution of the tile source.\n * If 0, the nearest resolution will be used. If 1, the nearest lower resolution\n * will be used. If -1, the nearest higher resolution will be used.\n * @return {number|undefined} Valid zoom level.\n */\n View.prototype.getConstrainedZoom = function (targetZoom, opt_direction) {\n var targetRes = this.getResolutionForZoom(targetZoom);\n return this.getZoomForResolution(this.getConstrainedResolution(targetRes, opt_direction));\n };\n /**\n * Get a valid resolution according to the current view constraints.\n * @param {number|undefined} targetResolution Target resolution.\n * @param {number=} [opt_direction=0] Indicate which resolution should be used\n * by a renderer if the view resolution does not match any resolution of the tile source.\n * If 0, the nearest resolution will be used. If 1, the nearest lower resolution\n * will be used. If -1, the nearest higher resolution will be used.\n * @return {number|undefined} Valid resolution.\n */\n View.prototype.getConstrainedResolution = function (targetResolution, opt_direction) {\n var direction = opt_direction || 0;\n var size = this.getSizeFromViewport_(this.getRotation());\n return this.constraints_.resolution(targetResolution, direction, size);\n };\n return View;\n}(BaseObject));\n/**\n * @param {Function} callback Callback.\n * @param {*} returnValue Return value.\n */\nfunction animationCallback(callback, returnValue) {\n setTimeout(function () {\n callback(returnValue);\n }, 0);\n}\n/**\n * @param {ViewOptions} options View options.\n * @return {import(\"./centerconstraint.js\").Type} The constraint.\n */\nexport function createCenterConstraint(options) {\n if (options.extent !== undefined) {\n var smooth = options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true;\n return createExtent(options.extent, options.constrainOnlyCenter, smooth);\n }\n var projection = createProjection(options.projection, 'EPSG:3857');\n if (options.multiWorld !== true && projection.isGlobal()) {\n var extent = projection.getExtent().slice();\n extent[0] = -Infinity;\n extent[2] = Infinity;\n return createExtent(extent, false, false);\n }\n return centerNone;\n}\n/**\n * @param {ViewOptions} options View options.\n * @return {{constraint: import(\"./resolutionconstraint.js\").Type, maxResolution: number,\n * minResolution: number, minZoom: number, zoomFactor: number}} The constraint.\n */\nexport function createResolutionConstraint(options) {\n var resolutionConstraint;\n var maxResolution;\n var minResolution;\n // TODO: move these to be ol constants\n // see https://github.com/openlayers/openlayers/issues/2076\n var defaultMaxZoom = 28;\n var defaultZoomFactor = 2;\n var minZoom = options.minZoom !== undefined ?\n options.minZoom : DEFAULT_MIN_ZOOM;\n var maxZoom = options.maxZoom !== undefined ?\n options.maxZoom : defaultMaxZoom;\n var zoomFactor = options.zoomFactor !== undefined ?\n options.zoomFactor : defaultZoomFactor;\n var multiWorld = options.multiWorld !== undefined ?\n options.multiWorld : false;\n var smooth = options.smoothResolutionConstraint !== undefined ? options.smoothResolutionConstraint : true;\n var projection = createProjection(options.projection, 'EPSG:3857');\n var projExtent = projection.getExtent();\n var constrainOnlyCenter = options.constrainOnlyCenter;\n var extent = options.extent;\n if (!multiWorld && !extent && projection.isGlobal()) {\n constrainOnlyCenter = false;\n extent = projExtent;\n }\n if (options.resolutions !== undefined) {\n var resolutions = options.resolutions;\n maxResolution = resolutions[minZoom];\n minResolution = resolutions[maxZoom] !== undefined ?\n resolutions[maxZoom] : resolutions[resolutions.length - 1];\n if (options.constrainResolution) {\n resolutionConstraint = createSnapToResolutions(resolutions, smooth, !constrainOnlyCenter && extent);\n }\n else {\n resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth, !constrainOnlyCenter && extent);\n }\n }\n else {\n // calculate the default min and max resolution\n var size = !projExtent ?\n // use an extent that can fit the whole world if need be\n 360 * METERS_PER_UNIT[Units.DEGREES] /\n projection.getMetersPerUnit() :\n Math.max(getWidth(projExtent), getHeight(projExtent));\n var defaultMaxResolution = size / DEFAULT_TILE_SIZE / Math.pow(defaultZoomFactor, DEFAULT_MIN_ZOOM);\n var defaultMinResolution = defaultMaxResolution / Math.pow(defaultZoomFactor, defaultMaxZoom - DEFAULT_MIN_ZOOM);\n // user provided maxResolution takes precedence\n maxResolution = options.maxResolution;\n if (maxResolution !== undefined) {\n minZoom = 0;\n }\n else {\n maxResolution = defaultMaxResolution / Math.pow(zoomFactor, minZoom);\n }\n // user provided minResolution takes precedence\n minResolution = options.minResolution;\n if (minResolution === undefined) {\n if (options.maxZoom !== undefined) {\n if (options.maxResolution !== undefined) {\n minResolution = maxResolution / Math.pow(zoomFactor, maxZoom);\n }\n else {\n minResolution = defaultMaxResolution / Math.pow(zoomFactor, maxZoom);\n }\n }\n else {\n minResolution = defaultMinResolution;\n }\n }\n // given discrete zoom levels, minResolution may be different than provided\n maxZoom = minZoom + Math.floor(Math.log(maxResolution / minResolution) / Math.log(zoomFactor));\n minResolution = maxResolution / Math.pow(zoomFactor, maxZoom - minZoom);\n if (options.constrainResolution) {\n resolutionConstraint = createSnapToPower(zoomFactor, maxResolution, minResolution, smooth, !constrainOnlyCenter && extent);\n }\n else {\n resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth, !constrainOnlyCenter && extent);\n }\n }\n return { constraint: resolutionConstraint, maxResolution: maxResolution,\n minResolution: minResolution, minZoom: minZoom, zoomFactor: zoomFactor };\n}\n/**\n * @param {ViewOptions} options View options.\n * @return {import(\"./rotationconstraint.js\").Type} Rotation constraint.\n */\nexport function createRotationConstraint(options) {\n var enableRotation = options.enableRotation !== undefined ?\n options.enableRotation : true;\n if (enableRotation) {\n var constrainRotation = options.constrainRotation;\n if (constrainRotation === undefined || constrainRotation === true) {\n return createSnapToZero();\n }\n else if (constrainRotation === false) {\n return rotationNone;\n }\n else if (typeof constrainRotation === 'number') {\n return createSnapToN(constrainRotation);\n }\n else {\n return rotationNone;\n }\n }\n else {\n return disable;\n }\n}\n/**\n * Determine if an animation involves no view change.\n * @param {Animation} animation The animation.\n * @return {boolean} The animation involves no view change.\n */\nexport function isNoopAnimation(animation) {\n if (animation.sourceCenter && animation.targetCenter) {\n if (!coordinatesEqual(animation.sourceCenter, animation.targetCenter)) {\n return false;\n }\n }\n if (animation.sourceResolution !== animation.targetResolution) {\n return false;\n }\n if (animation.sourceRotation !== animation.targetRotation) {\n return false;\n }\n return true;\n}\nexport default View;\n//# sourceMappingURL=View.js.map","/**\n * @module ol/dom\n */\n/**\n * Create an html canvas element and returns its 2d context.\n * @param {number=} opt_width Canvas width.\n * @param {number=} opt_height Canvas height.\n * @return {CanvasRenderingContext2D} The context.\n */\nexport function createCanvasContext2D(opt_width, opt_height) {\n var canvas = document.createElement('canvas');\n if (opt_width) {\n canvas.width = opt_width;\n }\n if (opt_height) {\n canvas.height = opt_height;\n }\n return canvas.getContext('2d');\n}\n/**\n * Get the current computed width for the given element including margin,\n * padding and border.\n * Equivalent to jQuery's `$(el).outerWidth(true)`.\n * @param {!HTMLElement} element Element.\n * @return {number} The width.\n */\nexport function outerWidth(element) {\n var width = element.offsetWidth;\n var style = getComputedStyle(element);\n width += parseInt(style.marginLeft, 10) + parseInt(style.marginRight, 10);\n return width;\n}\n/**\n * Get the current computed height for the given element including margin,\n * padding and border.\n * Equivalent to jQuery's `$(el).outerHeight(true)`.\n * @param {!HTMLElement} element Element.\n * @return {number} The height.\n */\nexport function outerHeight(element) {\n var height = element.offsetHeight;\n var style = getComputedStyle(element);\n height += parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10);\n return height;\n}\n/**\n * @param {Node} newNode Node to replace old node\n * @param {Node} oldNode The node to be replaced\n */\nexport function replaceNode(newNode, oldNode) {\n var parent = oldNode.parentNode;\n if (parent) {\n parent.replaceChild(newNode, oldNode);\n }\n}\n/**\n * @param {Node} node The node to remove.\n * @returns {Node} The node that was removed or null.\n */\nexport function removeNode(node) {\n return node && node.parentNode ? node.parentNode.removeChild(node) : null;\n}\n/**\n * @param {Node} node The node to remove the children from.\n */\nexport function removeChildren(node) {\n while (node.lastChild) {\n node.removeChild(node.lastChild);\n }\n}\n/**\n * Transform the children of a parent node so they match the\n * provided list of children. This function aims to efficiently\n * remove, add, and reorder child nodes while maintaining a simple\n * implementation (it is not guaranteed to minimize DOM operations).\n * @param {Node} node The parent node whose children need reworking.\n * @param {Array} children The desired children.\n */\nexport function replaceChildren(node, children) {\n var oldChildren = node.childNodes;\n for (var i = 0; true; ++i) {\n var oldChild = oldChildren[i];\n var newChild = children[i];\n // check if our work is done\n if (!oldChild && !newChild) {\n break;\n }\n // check if children match\n if (oldChild === newChild) {\n continue;\n }\n // check if a new child needs to be added\n if (!oldChild) {\n node.appendChild(newChild);\n continue;\n }\n // check if an old child needs to be removed\n if (!newChild) {\n node.removeChild(oldChild);\n --i;\n continue;\n }\n // reorder\n node.insertBefore(newChild, oldChild);\n }\n}\n//# sourceMappingURL=dom.js.map","/**\n * @module ol/layer/Property\n */\n/**\n * @enum {string}\n */\nexport default {\n OPACITY: 'opacity',\n VISIBLE: 'visible',\n EXTENT: 'extent',\n Z_INDEX: 'zIndex',\n MAX_RESOLUTION: 'maxResolution',\n MIN_RESOLUTION: 'minResolution',\n MAX_ZOOM: 'maxZoom',\n MIN_ZOOM: 'minZoom',\n SOURCE: 'source'\n};\n//# sourceMappingURL=Property.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/layer/Base\n */\nimport { abstract } from '../util.js';\nimport BaseObject from '../Object.js';\nimport LayerProperty from './Property.js';\nimport { clamp } from '../math.js';\nimport { assign } from '../obj.js';\nimport { assert } from '../asserts.js';\n/**\n * @typedef {Object} Options\n * @property {string} [className='ol-layer'] A CSS class name to set to the layer element.\n * @property {number} [opacity=1] Opacity (0, 1).\n * @property {boolean} [visible=true] Visibility.\n * @property {import(\"../extent.js\").Extent} [extent] The bounding extent for layer rendering. The layer will not be\n * rendered outside of this extent.\n * @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers\n * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed\n * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()`\n * method was used.\n * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be\n * visible.\n * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will\n * be visible.\n * @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be\n * visible.\n * @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will\n * be visible.\n */\n/**\n * @classdesc\n * Abstract base class; normally only used for creating subclasses and not\n * instantiated in apps.\n * Note that with {@link module:ol/layer/Base} and all its subclasses, any property set in\n * the options is set as a {@link module:ol/Object} property on the layer object, so\n * is observable, and has get/set accessors.\n *\n * @api\n */\nvar BaseLayer = /** @class */ (function (_super) {\n __extends(BaseLayer, _super);\n /**\n * @param {Options} options Layer options.\n */\n function BaseLayer(options) {\n var _this = _super.call(this) || this;\n /**\n * @type {Object}\n */\n var properties = assign({}, options);\n properties[LayerProperty.OPACITY] =\n options.opacity !== undefined ? options.opacity : 1;\n assert(typeof properties[LayerProperty.OPACITY] === 'number', 64); // Layer opacity must be a number\n properties[LayerProperty.VISIBLE] =\n options.visible !== undefined ? options.visible : true;\n properties[LayerProperty.Z_INDEX] = options.zIndex;\n properties[LayerProperty.MAX_RESOLUTION] =\n options.maxResolution !== undefined ? options.maxResolution : Infinity;\n properties[LayerProperty.MIN_RESOLUTION] =\n options.minResolution !== undefined ? options.minResolution : 0;\n properties[LayerProperty.MIN_ZOOM] =\n options.minZoom !== undefined ? options.minZoom : -Infinity;\n properties[LayerProperty.MAX_ZOOM] =\n options.maxZoom !== undefined ? options.maxZoom : Infinity;\n /**\n * @type {string}\n * @private\n */\n _this.className_ = properties.className !== undefined ? options.className : 'ol-layer';\n delete properties.className;\n _this.setProperties(properties);\n /**\n * @type {import(\"./Layer.js\").State}\n * @private\n */\n _this.state_ = null;\n return _this;\n }\n /**\n * @return {string} CSS class name.\n */\n BaseLayer.prototype.getClassName = function () {\n return this.className_;\n };\n /**\n * This method is not meant to be called by layers or layer renderers because the state\n * is incorrect if the layer is included in a layer group.\n *\n * @param {boolean=} opt_managed Layer is managed.\n * @return {import(\"./Layer.js\").State} Layer state.\n */\n BaseLayer.prototype.getLayerState = function (opt_managed) {\n /** @type {import(\"./Layer.js\").State} */\n var state = this.state_ || /** @type {?} */ ({\n layer: this,\n managed: opt_managed === undefined ? true : opt_managed\n });\n var zIndex = this.getZIndex();\n state.opacity = clamp(Math.round(this.getOpacity() * 100) / 100, 0, 1);\n state.sourceState = this.getSourceState();\n state.visible = this.getVisible();\n state.extent = this.getExtent();\n state.zIndex = zIndex !== undefined ? zIndex : (state.managed === false ? Infinity : 0);\n state.maxResolution = this.getMaxResolution();\n state.minResolution = Math.max(this.getMinResolution(), 0);\n state.minZoom = this.getMinZoom();\n state.maxZoom = this.getMaxZoom();\n this.state_ = state;\n return state;\n };\n /**\n * @abstract\n * @param {Array=} opt_array Array of layers (to be\n * modified in place).\n * @return {Array} Array of layers.\n */\n BaseLayer.prototype.getLayersArray = function (opt_array) {\n return abstract();\n };\n /**\n * @abstract\n * @param {Array=} opt_states Optional list of layer\n * states (to be modified in place).\n * @return {Array} List of layer states.\n */\n BaseLayer.prototype.getLayerStatesArray = function (opt_states) {\n return abstract();\n };\n /**\n * Return the {@link module:ol/extent~Extent extent} of the layer or `undefined` if it\n * will be visible regardless of extent.\n * @return {import(\"../extent.js\").Extent|undefined} The layer extent.\n * @observable\n * @api\n */\n BaseLayer.prototype.getExtent = function () {\n return (\n /** @type {import(\"../extent.js\").Extent|undefined} */ (this.get(LayerProperty.EXTENT)));\n };\n /**\n * Return the maximum resolution of the layer.\n * @return {number} The maximum resolution of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.getMaxResolution = function () {\n return /** @type {number} */ (this.get(LayerProperty.MAX_RESOLUTION));\n };\n /**\n * Return the minimum resolution of the layer.\n * @return {number} The minimum resolution of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.getMinResolution = function () {\n return /** @type {number} */ (this.get(LayerProperty.MIN_RESOLUTION));\n };\n /**\n * Return the minimum zoom level of the layer.\n * @return {number} The minimum zoom level of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.getMinZoom = function () {\n return /** @type {number} */ (this.get(LayerProperty.MIN_ZOOM));\n };\n /**\n * Return the maximum zoom level of the layer.\n * @return {number} The maximum zoom level of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.getMaxZoom = function () {\n return /** @type {number} */ (this.get(LayerProperty.MAX_ZOOM));\n };\n /**\n * Return the opacity of the layer (between 0 and 1).\n * @return {number} The opacity of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.getOpacity = function () {\n return /** @type {number} */ (this.get(LayerProperty.OPACITY));\n };\n /**\n * @abstract\n * @return {import(\"../source/State.js\").default} Source state.\n */\n BaseLayer.prototype.getSourceState = function () {\n return abstract();\n };\n /**\n * Return the visibility of the layer (`true` or `false`).\n * @return {boolean} The visibility of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.getVisible = function () {\n return /** @type {boolean} */ (this.get(LayerProperty.VISIBLE));\n };\n /**\n * Return the Z-index of the layer, which is used to order layers before\n * rendering. The default Z-index is 0.\n * @return {number} The Z-index of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.getZIndex = function () {\n return /** @type {number} */ (this.get(LayerProperty.Z_INDEX));\n };\n /**\n * Set the extent at which the layer is visible. If `undefined`, the layer\n * will be visible at all extents.\n * @param {import(\"../extent.js\").Extent|undefined} extent The extent of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.setExtent = function (extent) {\n this.set(LayerProperty.EXTENT, extent);\n };\n /**\n * Set the maximum resolution at which the layer is visible.\n * @param {number} maxResolution The maximum resolution of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.setMaxResolution = function (maxResolution) {\n this.set(LayerProperty.MAX_RESOLUTION, maxResolution);\n };\n /**\n * Set the minimum resolution at which the layer is visible.\n * @param {number} minResolution The minimum resolution of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.setMinResolution = function (minResolution) {\n this.set(LayerProperty.MIN_RESOLUTION, minResolution);\n };\n /**\n * Set the maximum zoom (exclusive) at which the layer is visible.\n * Note that the zoom levels for layer visibility are based on the\n * view zoom level, which may be different from a tile source zoom level.\n * @param {number} maxZoom The maximum zoom of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.setMaxZoom = function (maxZoom) {\n this.set(LayerProperty.MAX_ZOOM, maxZoom);\n };\n /**\n * Set the minimum zoom (inclusive) at which the layer is visible.\n * Note that the zoom levels for layer visibility are based on the\n * view zoom level, which may be different from a tile source zoom level.\n * @param {number} minZoom The minimum zoom of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.setMinZoom = function (minZoom) {\n this.set(LayerProperty.MIN_ZOOM, minZoom);\n };\n /**\n * Set the opacity of the layer, allowed values range from 0 to 1.\n * @param {number} opacity The opacity of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.setOpacity = function (opacity) {\n assert(typeof opacity === 'number', 64); // Layer opacity must be a number\n this.set(LayerProperty.OPACITY, opacity);\n };\n /**\n * Set the visibility of the layer (`true` or `false`).\n * @param {boolean} visible The visibility of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.setVisible = function (visible) {\n this.set(LayerProperty.VISIBLE, visible);\n };\n /**\n * Set Z-index of the layer, which is used to order layers before rendering.\n * The default Z-index is 0.\n * @param {number} zindex The z-index of the layer.\n * @observable\n * @api\n */\n BaseLayer.prototype.setZIndex = function (zindex) {\n this.set(LayerProperty.Z_INDEX, zindex);\n };\n /**\n * @inheritDoc\n */\n BaseLayer.prototype.disposeInternal = function () {\n if (this.state_) {\n this.state_.layer = null;\n this.state_ = null;\n }\n _super.prototype.disposeInternal.call(this);\n };\n return BaseLayer;\n}(BaseObject));\nexport default BaseLayer;\n//# sourceMappingURL=Base.js.map","/**\n * @module ol/source/State\n */\n/**\n * @enum {string}\n * State of the source, one of 'undefined', 'loading', 'ready' or 'error'.\n */\nexport default {\n UNDEFINED: 'undefined',\n LOADING: 'loading',\n READY: 'ready',\n ERROR: 'error'\n};\n//# sourceMappingURL=State.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/layer/Group\n */\nimport { getUid } from '../util.js';\nimport Collection from '../Collection.js';\nimport CollectionEventType from '../CollectionEventType.js';\nimport { getChangeEventType } from '../Object.js';\nimport ObjectEventType from '../ObjectEventType.js';\nimport { assert } from '../asserts.js';\nimport { listen, unlistenByKey } from '../events.js';\nimport EventType from '../events/EventType.js';\nimport { getIntersection } from '../extent.js';\nimport BaseLayer from './Base.js';\nimport { assign, clear } from '../obj.js';\nimport SourceState from '../source/State.js';\n/**\n * @typedef {Object} Options\n * @property {number} [opacity=1] Opacity (0, 1).\n * @property {boolean} [visible=true] Visibility.\n * @property {import(\"../extent.js\").Extent} [extent] The bounding extent for layer rendering. The layer will not be\n * rendered outside of this extent.\n * @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers\n * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed\n * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()`\n * method was used.\n * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be\n * visible.\n * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will\n * be visible.\n * @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be\n * visible.\n * @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will\n * be visible.\n * @property {Array|import(\"../Collection.js\").default} [layers] Child layers.\n */\n/**\n * @enum {string}\n * @private\n */\nvar Property = {\n LAYERS: 'layers'\n};\n/**\n * @classdesc\n * A {@link module:ol/Collection~Collection} of layers that are handled together.\n *\n * A generic `change` event is triggered when the group/Collection changes.\n *\n * @api\n */\nvar LayerGroup = /** @class */ (function (_super) {\n __extends(LayerGroup, _super);\n /**\n * @param {Options=} opt_options Layer options.\n */\n function LayerGroup(opt_options) {\n var _this = this;\n var options = opt_options || {};\n var baseOptions = /** @type {Options} */ (assign({}, options));\n delete baseOptions.layers;\n var layers = options.layers;\n _this = _super.call(this, baseOptions) || this;\n /**\n * @private\n * @type {Array}\n */\n _this.layersListenerKeys_ = [];\n /**\n * @private\n * @type {Object>}\n */\n _this.listenerKeys_ = {};\n _this.addEventListener(getChangeEventType(Property.LAYERS), _this.handleLayersChanged_);\n if (layers) {\n if (Array.isArray(layers)) {\n layers = new Collection(layers.slice(), { unique: true });\n }\n else {\n assert(typeof /** @type {?} */ (layers).getArray === 'function', 43); // Expected `layers` to be an array or a `Collection`\n }\n }\n else {\n layers = new Collection(undefined, { unique: true });\n }\n _this.setLayers(layers);\n return _this;\n }\n /**\n * @private\n */\n LayerGroup.prototype.handleLayerChange_ = function () {\n this.changed();\n };\n /**\n * @private\n */\n LayerGroup.prototype.handleLayersChanged_ = function () {\n this.layersListenerKeys_.forEach(unlistenByKey);\n this.layersListenerKeys_.length = 0;\n var layers = this.getLayers();\n this.layersListenerKeys_.push(listen(layers, CollectionEventType.ADD, this.handleLayersAdd_, this), listen(layers, CollectionEventType.REMOVE, this.handleLayersRemove_, this));\n for (var id in this.listenerKeys_) {\n this.listenerKeys_[id].forEach(unlistenByKey);\n }\n clear(this.listenerKeys_);\n var layersArray = layers.getArray();\n for (var i = 0, ii = layersArray.length; i < ii; i++) {\n var layer = layersArray[i];\n this.listenerKeys_[getUid(layer)] = [\n listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this),\n listen(layer, EventType.CHANGE, this.handleLayerChange_, this)\n ];\n }\n this.changed();\n };\n /**\n * @param {import(\"../Collection.js\").CollectionEvent} collectionEvent CollectionEvent.\n * @private\n */\n LayerGroup.prototype.handleLayersAdd_ = function (collectionEvent) {\n var layer = /** @type {import(\"./Base.js\").default} */ (collectionEvent.element);\n this.listenerKeys_[getUid(layer)] = [\n listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this),\n listen(layer, EventType.CHANGE, this.handleLayerChange_, this)\n ];\n this.changed();\n };\n /**\n * @param {import(\"../Collection.js\").CollectionEvent} collectionEvent CollectionEvent.\n * @private\n */\n LayerGroup.prototype.handleLayersRemove_ = function (collectionEvent) {\n var layer = /** @type {import(\"./Base.js\").default} */ (collectionEvent.element);\n var key = getUid(layer);\n this.listenerKeys_[key].forEach(unlistenByKey);\n delete this.listenerKeys_[key];\n this.changed();\n };\n /**\n * Returns the {@link module:ol/Collection collection} of {@link module:ol/layer/Layer~Layer layers}\n * in this group.\n * @return {!import(\"../Collection.js\").default} Collection of\n * {@link module:ol/layer/Base layers} that are part of this group.\n * @observable\n * @api\n */\n LayerGroup.prototype.getLayers = function () {\n return (\n /** @type {!import(\"../Collection.js\").default} */ (this.get(Property.LAYERS)));\n };\n /**\n * Set the {@link module:ol/Collection collection} of {@link module:ol/layer/Layer~Layer layers}\n * in this group.\n * @param {!import(\"../Collection.js\").default} layers Collection of\n * {@link module:ol/layer/Base layers} that are part of this group.\n * @observable\n * @api\n */\n LayerGroup.prototype.setLayers = function (layers) {\n this.set(Property.LAYERS, layers);\n };\n /**\n * @inheritDoc\n */\n LayerGroup.prototype.getLayersArray = function (opt_array) {\n var array = opt_array !== undefined ? opt_array : [];\n this.getLayers().forEach(function (layer) {\n layer.getLayersArray(array);\n });\n return array;\n };\n /**\n * @inheritDoc\n */\n LayerGroup.prototype.getLayerStatesArray = function (opt_states) {\n var states = opt_states !== undefined ? opt_states : [];\n var pos = states.length;\n this.getLayers().forEach(function (layer) {\n layer.getLayerStatesArray(states);\n });\n var ownLayerState = this.getLayerState();\n for (var i = pos, ii = states.length; i < ii; i++) {\n var layerState = states[i];\n layerState.opacity *= ownLayerState.opacity;\n layerState.visible = layerState.visible && ownLayerState.visible;\n layerState.maxResolution = Math.min(layerState.maxResolution, ownLayerState.maxResolution);\n layerState.minResolution = Math.max(layerState.minResolution, ownLayerState.minResolution);\n layerState.minZoom = Math.max(layerState.minZoom, ownLayerState.minZoom);\n layerState.maxZoom = Math.min(layerState.maxZoom, ownLayerState.maxZoom);\n if (ownLayerState.extent !== undefined) {\n if (layerState.extent !== undefined) {\n layerState.extent = getIntersection(layerState.extent, ownLayerState.extent);\n }\n else {\n layerState.extent = ownLayerState.extent;\n }\n }\n }\n return states;\n };\n /**\n * @inheritDoc\n */\n LayerGroup.prototype.getSourceState = function () {\n return SourceState.READY;\n };\n return LayerGroup;\n}(BaseLayer));\nexport default LayerGroup;\n//# sourceMappingURL=Group.js.map","/**\n * @module ol/size\n */\n/**\n * An array of numbers representing a size: `[width, height]`.\n * @typedef {Array} Size\n * @api\n */\n/**\n * Returns a buffered size.\n * @param {Size} size Size.\n * @param {number} num The amount by which to buffer.\n * @param {Size=} opt_size Optional reusable size array.\n * @return {Size} The buffered size.\n */\nexport function buffer(size, num, opt_size) {\n if (opt_size === undefined) {\n opt_size = [0, 0];\n }\n opt_size[0] = size[0] + 2 * num;\n opt_size[1] = size[1] + 2 * num;\n return opt_size;\n}\n/**\n * Determines if a size has a positive area.\n * @param {Size} size The size to test.\n * @return {boolean} The size has a positive area.\n */\nexport function hasArea(size) {\n return size[0] > 0 && size[1] > 0;\n}\n/**\n * Returns a size scaled by a ratio. The result will be an array of integers.\n * @param {Size} size Size.\n * @param {number} ratio Ratio.\n * @param {Size=} opt_size Optional reusable size array.\n * @return {Size} The scaled size.\n */\nexport function scale(size, ratio, opt_size) {\n if (opt_size === undefined) {\n opt_size = [0, 0];\n }\n opt_size[0] = (size[0] * ratio + 0.5) | 0;\n opt_size[1] = (size[1] * ratio + 0.5) | 0;\n return opt_size;\n}\n/**\n * Returns an `Size` array for the passed in number (meaning: square) or\n * `Size` array.\n * (meaning: non-square),\n * @param {number|Size} size Width and height.\n * @param {Size=} opt_size Optional reusable size array.\n * @return {Size} Size.\n * @api\n */\nexport function toSize(size, opt_size) {\n if (Array.isArray(size)) {\n return size;\n }\n else {\n if (opt_size === undefined) {\n opt_size = [size, size];\n }\n else {\n opt_size[0] = size;\n opt_size[1] = size;\n }\n return opt_size;\n }\n}\n//# sourceMappingURL=size.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/PluggableMap\n */\nimport { getUid } from './util.js';\nimport Collection from './Collection.js';\nimport CollectionEventType from './CollectionEventType.js';\nimport MapBrowserEvent from './MapBrowserEvent.js';\nimport MapBrowserEventHandler from './MapBrowserEventHandler.js';\nimport MapBrowserEventType from './MapBrowserEventType.js';\nimport MapEvent from './MapEvent.js';\nimport MapEventType from './MapEventType.js';\nimport MapProperty from './MapProperty.js';\nimport RenderEventType from './render/EventType.js';\nimport BaseObject, { getChangeEventType } from './Object.js';\nimport ObjectEventType from './ObjectEventType.js';\nimport TileQueue from './TileQueue.js';\nimport View from './View.js';\nimport ViewHint from './ViewHint.js';\nimport { assert } from './asserts.js';\nimport { removeNode } from './dom.js';\nimport { listen, unlistenByKey } from './events.js';\nimport EventType from './events/EventType.js';\nimport { clone, createOrUpdateEmpty, equals, getForViewAndSize, isEmpty } from './extent.js';\nimport { TRUE } from './functions.js';\nimport { DEVICE_PIXEL_RATIO, IMAGE_DECODE } from './has.js';\nimport LayerGroup from './layer/Group.js';\nimport { hasArea } from './size.js';\nimport { DROP } from './structs/PriorityQueue.js';\nimport { create as createTransform, apply as applyTransform } from './transform.js';\nimport { toUserCoordinate, fromUserCoordinate } from './proj.js';\n/**\n * State of the current frame. Only `pixelRatio`, `time` and `viewState` should\n * be used in applications.\n * @typedef {Object} FrameState\n * @property {number} pixelRatio The pixel ratio of the frame.\n * @property {number} time The time when rendering of the frame was requested.\n * @property {import(\"./View.js\").State} viewState The state of the current view.\n * @property {boolean} animate\n * @property {import(\"./transform.js\").Transform} coordinateToPixelTransform\n * @property {null|import(\"./extent.js\").Extent} extent\n * @property {Array} declutterItems\n * @property {number} index\n * @property {Array} layerStatesArray\n * @property {number} layerIndex\n * @property {import(\"./transform.js\").Transform} pixelToCoordinateTransform\n * @property {Array} postRenderFunctions\n * @property {import(\"./size.js\").Size} size\n * @property {TileQueue} tileQueue\n * @property {!Object>} usedTiles\n * @property {Array} viewHints\n * @property {!Object>} wantedTiles\n */\n/**\n * @typedef {Object} DeclutterItems\n * @property {Array<*>} items Declutter items of an executor.\n * @property {number} opacity Layer opacity.\n */\n/**\n * @typedef {function(PluggableMap, ?FrameState): any} PostRenderFunction\n */\n/**\n * @typedef {Object} AtPixelOptions\n * @property {undefined|function(import(\"./layer/Layer.js\").default): boolean} [layerFilter] Layer filter\n * function. The filter function will receive one argument, the\n * {@link module:ol/layer/Layer layer-candidate} and it should return a boolean value.\n * Only layers which are visible and for which this function returns `true`\n * will be tested for features. By default, all visible layers will be tested.\n * @property {number} [hitTolerance=0] Hit-detection tolerance in pixels. Pixels\n * inside the radius around the given position will be checked for features.\n * @property {boolean} [checkWrapped=true] Check-Wrapped Will check for for wrapped geometries inside the range of\n * +/- 1 world width. Works only if a projection is used that can be wrapped.\n */\n/**\n * @typedef {Object} MapOptionsInternal\n * @property {Collection} [controls]\n * @property {Collection} [interactions]\n * @property {HTMLElement|Document} keyboardEventTarget\n * @property {Collection} overlays\n * @property {Object} values\n */\n/**\n * Object literal with config options for the map.\n * @typedef {Object} MapOptions\n * @property {Collection|Array} [controls]\n * Controls initially added to the map. If not specified,\n * {@link module:ol/control~defaults} is used.\n * @property {number} [pixelRatio=window.devicePixelRatio] The ratio between\n * physical pixels and device-independent pixels (dips) on the device.\n * @property {Collection|Array} [interactions]\n * Interactions that are initially added to the map. If not specified,\n * {@link module:ol/interaction~defaults} is used.\n * @property {HTMLElement|Document|string} [keyboardEventTarget] The element to\n * listen to keyboard events on. This determines when the `KeyboardPan` and\n * `KeyboardZoom` interactions trigger. For example, if this option is set to\n * `document` the keyboard interactions will always trigger. If this option is\n * not specified, the element the library listens to keyboard events on is the\n * map target (i.e. the user-provided div for the map). If this is not\n * `document`, the target element needs to be focused for key events to be\n * emitted, requiring that the target element has a `tabindex` attribute.\n * @property {Array|Collection|LayerGroup} [layers]\n * Layers. If this is not defined, a map with no layers will be rendered. Note\n * that layers are rendered in the order supplied, so if you want, for example,\n * a vector layer to appear on top of a tile layer, it must come after the tile\n * layer.\n * @property {number} [maxTilesLoading=16] Maximum number tiles to load\n * simultaneously.\n * @property {number} [moveTolerance=1] The minimum distance in pixels the\n * cursor must move to be detected as a map move event instead of a click.\n * Increasing this value can make it easier to click on the map.\n * @property {Collection|Array} [overlays]\n * Overlays initially added to the map. By default, no overlays are added.\n * @property {HTMLElement|string} [target] The container for the map, either the\n * element itself or the `id` of the element. If not specified at construction\n * time, {@link module:ol/Map~Map#setTarget} must be called for the map to be\n * rendered.\n * @property {View} [view] The map's view. No layer sources will be\n * fetched unless this is specified at construction time or through\n * {@link module:ol/Map~Map#setView}.\n */\n/**\n * @param {HTMLElement} element Element.\n * @param {string} touchAction Value for `touch-action'.\n */\nfunction setTouchAction(element, touchAction) {\n element.style.msTouchAction = touchAction;\n element.style.touchAction = touchAction;\n element.setAttribute('touch-action', touchAction);\n}\n/**\n * @fires import(\"./MapBrowserEvent.js\").MapBrowserEvent\n * @fires import(\"./MapEvent.js\").MapEvent\n * @fires import(\"./render/Event.js\").default#precompose\n * @fires import(\"./render/Event.js\").default#postcompose\n * @fires import(\"./render/Event.js\").default#rendercomplete\n * @api\n */\nvar PluggableMap = /** @class */ (function (_super) {\n __extends(PluggableMap, _super);\n /**\n * @param {MapOptions} options Map options.\n */\n function PluggableMap(options) {\n var _this = _super.call(this) || this;\n var optionsInternal = createOptionsInternal(options);\n /** @private */\n _this.boundHandleBrowserEvent_ = _this.handleBrowserEvent.bind(_this);\n /**\n * @type {number}\n * @private\n */\n _this.maxTilesLoading_ = options.maxTilesLoading !== undefined ? options.maxTilesLoading : 16;\n /**\n * @private\n * @type {number}\n */\n _this.pixelRatio_ = options.pixelRatio !== undefined ?\n options.pixelRatio : DEVICE_PIXEL_RATIO;\n /**\n * @private\n * @type {*}\n */\n _this.postRenderTimeoutHandle_;\n /**\n * @private\n * @type {number|undefined}\n */\n _this.animationDelayKey_;\n /**\n * @private\n */\n _this.animationDelay_ = function () {\n this.animationDelayKey_ = undefined;\n this.renderFrame_(Date.now());\n }.bind(_this);\n /**\n * @private\n * @type {import(\"./transform.js\").Transform}\n */\n _this.coordinateToPixelTransform_ = createTransform();\n /**\n * @private\n * @type {import(\"./transform.js\").Transform}\n */\n _this.pixelToCoordinateTransform_ = createTransform();\n /**\n * @private\n * @type {number}\n */\n _this.frameIndex_ = 0;\n /**\n * @private\n * @type {?FrameState}\n */\n _this.frameState_ = null;\n /**\n * The extent at the previous 'moveend' event.\n * @private\n * @type {import(\"./extent.js\").Extent}\n */\n _this.previousExtent_ = null;\n /**\n * @private\n * @type {?import(\"./events.js\").EventsKey}\n */\n _this.viewPropertyListenerKey_ = null;\n /**\n * @private\n * @type {?import(\"./events.js\").EventsKey}\n */\n _this.viewChangeListenerKey_ = null;\n /**\n * @private\n * @type {?Array}\n */\n _this.layerGroupPropertyListenerKeys_ = null;\n /**\n * @private\n * @type {!HTMLElement}\n */\n _this.viewport_ = document.createElement('div');\n _this.viewport_.className = 'ol-viewport' + ('ontouchstart' in window ? ' ol-touch' : '');\n _this.viewport_.style.position = 'relative';\n _this.viewport_.style.overflow = 'hidden';\n _this.viewport_.style.width = '100%';\n _this.viewport_.style.height = '100%';\n /**\n * @private\n * @type {!HTMLElement}\n */\n _this.overlayContainer_ = document.createElement('div');\n _this.overlayContainer_.style.position = 'absolute';\n _this.overlayContainer_.style.zIndex = '0';\n _this.overlayContainer_.style.width = '100%';\n _this.overlayContainer_.style.height = '100%';\n _this.overlayContainer_.className = 'ol-overlaycontainer';\n _this.viewport_.appendChild(_this.overlayContainer_);\n /**\n * @private\n * @type {!HTMLElement}\n */\n _this.overlayContainerStopEvent_ = document.createElement('div');\n _this.overlayContainerStopEvent_.style.position = 'absolute';\n _this.overlayContainerStopEvent_.style.zIndex = '0';\n _this.overlayContainerStopEvent_.style.width = '100%';\n _this.overlayContainerStopEvent_.style.height = '100%';\n _this.overlayContainerStopEvent_.className = 'ol-overlaycontainer-stopevent';\n _this.viewport_.appendChild(_this.overlayContainerStopEvent_);\n /**\n * @private\n * @type {MapBrowserEventHandler}\n */\n _this.mapBrowserEventHandler_ = new MapBrowserEventHandler(_this, options.moveTolerance);\n var handleMapBrowserEvent = _this.handleMapBrowserEvent.bind(_this);\n for (var key in MapBrowserEventType) {\n _this.mapBrowserEventHandler_.addEventListener(MapBrowserEventType[key], handleMapBrowserEvent);\n }\n /**\n * @private\n * @type {HTMLElement|Document}\n */\n _this.keyboardEventTarget_ = optionsInternal.keyboardEventTarget;\n /**\n * @private\n * @type {?Array}\n */\n _this.keyHandlerKeys_ = null;\n /**\n * @private\n * @type {?Array}\n */\n _this.focusHandlerKeys_ = null;\n var handleBrowserEvent = _this.handleBrowserEvent.bind(_this);\n _this.viewport_.addEventListener(EventType.CONTEXTMENU, handleBrowserEvent, false);\n _this.viewport_.addEventListener(EventType.WHEEL, handleBrowserEvent, false);\n /**\n * @type {Collection}\n * @protected\n */\n _this.controls = optionsInternal.controls || new Collection();\n /**\n * @type {Collection}\n * @protected\n */\n _this.interactions = optionsInternal.interactions || new Collection();\n /**\n * @type {Collection}\n * @private\n */\n _this.overlays_ = optionsInternal.overlays;\n /**\n * A lookup of overlays by id.\n * @private\n * @type {Object}\n */\n _this.overlayIdIndex_ = {};\n /**\n * @type {import(\"./renderer/Map.js\").default}\n * @private\n */\n _this.renderer_ = null;\n /**\n * @type {undefined|function(Event): void}\n * @private\n */\n _this.handleResize_;\n /**\n * @private\n * @type {!Array}\n */\n _this.postRenderFunctions_ = [];\n /**\n * @private\n * @type {TileQueue}\n */\n _this.tileQueue_ = new TileQueue(_this.getTilePriority.bind(_this), _this.handleTileChange_.bind(_this));\n _this.addEventListener(getChangeEventType(MapProperty.LAYERGROUP), _this.handleLayerGroupChanged_);\n _this.addEventListener(getChangeEventType(MapProperty.VIEW), _this.handleViewChanged_);\n _this.addEventListener(getChangeEventType(MapProperty.SIZE), _this.handleSizeChanged_);\n _this.addEventListener(getChangeEventType(MapProperty.TARGET), _this.handleTargetChanged_);\n // setProperties will trigger the rendering of the map if the map\n // is \"defined\" already.\n _this.setProperties(optionsInternal.values);\n _this.controls.forEach(\n /**\n * @param {import(\"./control/Control.js\").default} control Control.\n * @this {PluggableMap}\n */\n function (control) {\n control.setMap(this);\n }.bind(_this));\n _this.controls.addEventListener(CollectionEventType.ADD, \n /**\n * @param {import(\"./Collection.js\").CollectionEvent} event CollectionEvent.\n */\n function (event) {\n event.element.setMap(this);\n }.bind(_this));\n _this.controls.addEventListener(CollectionEventType.REMOVE, \n /**\n * @param {import(\"./Collection.js\").CollectionEvent} event CollectionEvent.\n */\n function (event) {\n event.element.setMap(null);\n }.bind(_this));\n _this.interactions.forEach(\n /**\n * @param {import(\"./interaction/Interaction.js\").default} interaction Interaction.\n * @this {PluggableMap}\n */\n function (interaction) {\n interaction.setMap(this);\n }.bind(_this));\n _this.interactions.addEventListener(CollectionEventType.ADD, \n /**\n * @param {import(\"./Collection.js\").CollectionEvent} event CollectionEvent.\n */\n function (event) {\n event.element.setMap(this);\n }.bind(_this));\n _this.interactions.addEventListener(CollectionEventType.REMOVE, \n /**\n * @param {import(\"./Collection.js\").CollectionEvent} event CollectionEvent.\n */\n function (event) {\n event.element.setMap(null);\n }.bind(_this));\n _this.overlays_.forEach(_this.addOverlayInternal_.bind(_this));\n _this.overlays_.addEventListener(CollectionEventType.ADD, \n /**\n * @param {import(\"./Collection.js\").CollectionEvent} event CollectionEvent.\n */\n function (event) {\n this.addOverlayInternal_(/** @type {import(\"./Overlay.js\").default} */ (event.element));\n }.bind(_this));\n _this.overlays_.addEventListener(CollectionEventType.REMOVE, \n /**\n * @param {import(\"./Collection.js\").CollectionEvent} event CollectionEvent.\n */\n function (event) {\n var overlay = /** @type {import(\"./Overlay.js\").default} */ (event.element);\n var id = overlay.getId();\n if (id !== undefined) {\n delete this.overlayIdIndex_[id.toString()];\n }\n event.element.setMap(null);\n }.bind(_this));\n return _this;\n }\n /**\n * @abstract\n * @return {import(\"./renderer/Map.js\").default} The map renderer\n */\n PluggableMap.prototype.createRenderer = function () {\n throw new Error('Use a map type that has a createRenderer method');\n };\n /**\n * Add the given control to the map.\n * @param {import(\"./control/Control.js\").default} control Control.\n * @api\n */\n PluggableMap.prototype.addControl = function (control) {\n this.getControls().push(control);\n };\n /**\n * Add the given interaction to the map. If you want to add an interaction\n * at another point of the collection use `getInteraction()` and the methods\n * available on {@link module:ol/Collection~Collection}. This can be used to\n * stop the event propagation from the handleEvent function. The interactions\n * get to handle the events in the reverse order of this collection.\n * @param {import(\"./interaction/Interaction.js\").default} interaction Interaction to add.\n * @api\n */\n PluggableMap.prototype.addInteraction = function (interaction) {\n this.getInteractions().push(interaction);\n };\n /**\n * Adds the given layer to the top of this map. If you want to add a layer\n * elsewhere in the stack, use `getLayers()` and the methods available on\n * {@link module:ol/Collection~Collection}.\n * @param {import(\"./layer/Base.js\").default} layer Layer.\n * @api\n */\n PluggableMap.prototype.addLayer = function (layer) {\n var layers = this.getLayerGroup().getLayers();\n layers.push(layer);\n };\n /**\n * Add the given overlay to the map.\n * @param {import(\"./Overlay.js\").default} overlay Overlay.\n * @api\n */\n PluggableMap.prototype.addOverlay = function (overlay) {\n this.getOverlays().push(overlay);\n };\n /**\n * This deals with map's overlay collection changes.\n * @param {import(\"./Overlay.js\").default} overlay Overlay.\n * @private\n */\n PluggableMap.prototype.addOverlayInternal_ = function (overlay) {\n var id = overlay.getId();\n if (id !== undefined) {\n this.overlayIdIndex_[id.toString()] = overlay;\n }\n overlay.setMap(this);\n };\n /**\n *\n * @inheritDoc\n */\n PluggableMap.prototype.disposeInternal = function () {\n this.mapBrowserEventHandler_.dispose();\n this.viewport_.removeEventListener(EventType.CONTEXTMENU, this.boundHandleBrowserEvent_);\n this.viewport_.removeEventListener(EventType.WHEEL, this.boundHandleBrowserEvent_);\n if (this.handleResize_ !== undefined) {\n removeEventListener(EventType.RESIZE, this.handleResize_, false);\n this.handleResize_ = undefined;\n }\n this.setTarget(null);\n _super.prototype.disposeInternal.call(this);\n };\n /**\n * Detect features that intersect a pixel on the viewport, and execute a\n * callback with each intersecting feature. Layers included in the detection can\n * be configured through the `layerFilter` option in `opt_options`.\n * @param {import(\"./pixel.js\").Pixel} pixel Pixel.\n * @param {function(this: S, import(\"./Feature.js\").FeatureLike,\n * import(\"./layer/Layer.js\").default): T} callback Feature callback. The callback will be\n * called with two arguments. The first argument is one\n * {@link module:ol/Feature feature} or\n * {@link module:ol/render/Feature render feature} at the pixel, the second is\n * the {@link module:ol/layer/Layer layer} of the feature and will be null for\n * unmanaged layers. To stop detection, callback functions can return a\n * truthy value.\n * @param {AtPixelOptions=} opt_options Optional options.\n * @return {T|undefined} Callback result, i.e. the return value of last\n * callback execution, or the first truthy callback return value.\n * @template S,T\n * @api\n */\n PluggableMap.prototype.forEachFeatureAtPixel = function (pixel, callback, opt_options) {\n if (!this.frameState_) {\n return;\n }\n var coordinate = this.getCoordinateFromPixelInternal(pixel);\n opt_options = opt_options !== undefined ? opt_options : {};\n var hitTolerance = opt_options.hitTolerance !== undefined ?\n opt_options.hitTolerance * this.frameState_.pixelRatio : 0;\n var layerFilter = opt_options.layerFilter !== undefined ?\n opt_options.layerFilter : TRUE;\n var checkWrapped = opt_options.checkWrapped !== false;\n return this.renderer_.forEachFeatureAtCoordinate(coordinate, this.frameState_, hitTolerance, checkWrapped, callback, null, layerFilter, null);\n };\n /**\n * Get all features that intersect a pixel on the viewport.\n * @param {import(\"./pixel.js\").Pixel} pixel Pixel.\n * @param {AtPixelOptions=} opt_options Optional options.\n * @return {Array} The detected features or\n * an empty array if none were found.\n * @api\n */\n PluggableMap.prototype.getFeaturesAtPixel = function (pixel, opt_options) {\n var features = [];\n this.forEachFeatureAtPixel(pixel, function (feature) {\n features.push(feature);\n }, opt_options);\n return features;\n };\n /**\n * Detect layers that have a color value at a pixel on the viewport, and\n * execute a callback with each matching layer. Layers included in the\n * detection can be configured through `opt_layerFilter`.\n *\n * Note: this may give false positives unless the map layers have had different `className`\n * properties assigned to them.\n *\n * @param {import(\"./pixel.js\").Pixel} pixel Pixel.\n * @param {function(this: S, import(\"./layer/Layer.js\").default, (Uint8ClampedArray|Uint8Array)): T} callback\n * Layer callback. This callback will receive two arguments: first is the\n * {@link module:ol/layer/Layer layer}, second argument is an array representing\n * [R, G, B, A] pixel values (0 - 255) and will be `null` for layer types\n * that do not currently support this argument. To stop detection, callback\n * functions can return a truthy value.\n * @param {AtPixelOptions=} opt_options Configuration options.\n * @return {T|undefined} Callback result, i.e. the return value of last\n * callback execution, or the first truthy callback return value.\n * @template S,T\n * @api\n */\n PluggableMap.prototype.forEachLayerAtPixel = function (pixel, callback, opt_options) {\n if (!this.frameState_) {\n return;\n }\n var options = opt_options || {};\n var hitTolerance = options.hitTolerance !== undefined ?\n options.hitTolerance * this.frameState_.pixelRatio : 0;\n var layerFilter = options.layerFilter || TRUE;\n return this.renderer_.forEachLayerAtPixel(pixel, this.frameState_, hitTolerance, callback, layerFilter);\n };\n /**\n * Detect if features intersect a pixel on the viewport. Layers included in the\n * detection can be configured through `opt_layerFilter`.\n * @param {import(\"./pixel.js\").Pixel} pixel Pixel.\n * @param {AtPixelOptions=} opt_options Optional options.\n * @return {boolean} Is there a feature at the given pixel?\n * @api\n */\n PluggableMap.prototype.hasFeatureAtPixel = function (pixel, opt_options) {\n if (!this.frameState_) {\n return false;\n }\n var coordinate = this.getCoordinateFromPixelInternal(pixel);\n opt_options = opt_options !== undefined ? opt_options : {};\n var layerFilter = opt_options.layerFilter !== undefined ? opt_options.layerFilter : TRUE;\n var hitTolerance = opt_options.hitTolerance !== undefined ?\n opt_options.hitTolerance * this.frameState_.pixelRatio : 0;\n var checkWrapped = opt_options.checkWrapped !== false;\n return this.renderer_.hasFeatureAtCoordinate(coordinate, this.frameState_, hitTolerance, checkWrapped, layerFilter, null);\n };\n /**\n * Returns the coordinate in user projection for a browser event.\n * @param {Event} event Event.\n * @return {import(\"./coordinate.js\").Coordinate} Coordinate.\n * @api\n */\n PluggableMap.prototype.getEventCoordinate = function (event) {\n return this.getCoordinateFromPixel(this.getEventPixel(event));\n };\n /**\n * Returns the coordinate in view projection for a browser event.\n * @param {Event} event Event.\n * @return {import(\"./coordinate.js\").Coordinate} Coordinate.\n */\n PluggableMap.prototype.getEventCoordinateInternal = function (event) {\n return this.getCoordinateFromPixelInternal(this.getEventPixel(event));\n };\n /**\n * Returns the map pixel position for a browser event relative to the viewport.\n * @param {Event|TouchEvent} event Event.\n * @return {import(\"./pixel.js\").Pixel} Pixel.\n * @api\n */\n PluggableMap.prototype.getEventPixel = function (event) {\n var viewportPosition = this.viewport_.getBoundingClientRect();\n var eventPosition = 'changedTouches' in event ?\n /** @type {TouchEvent} */ (event).changedTouches[0] :\n /** @type {MouseEvent} */ (event);\n return [\n eventPosition.clientX - viewportPosition.left,\n eventPosition.clientY - viewportPosition.top\n ];\n };\n /**\n * Get the target in which this map is rendered.\n * Note that this returns what is entered as an option or in setTarget:\n * if that was an element, it returns an element; if a string, it returns that.\n * @return {HTMLElement|string|undefined} The Element or id of the Element that the\n * map is rendered in.\n * @observable\n * @api\n */\n PluggableMap.prototype.getTarget = function () {\n return /** @type {HTMLElement|string|undefined} */ (this.get(MapProperty.TARGET));\n };\n /**\n * Get the DOM element into which this map is rendered. In contrast to\n * `getTarget` this method always return an `Element`, or `null` if the\n * map has no target.\n * @return {HTMLElement} The element that the map is rendered in.\n * @api\n */\n PluggableMap.prototype.getTargetElement = function () {\n var target = this.getTarget();\n if (target !== undefined) {\n return typeof target === 'string' ? document.getElementById(target) : target;\n }\n else {\n return null;\n }\n };\n /**\n * Get the coordinate for a given pixel. This returns a coordinate in the\n * user projection.\n * @param {import(\"./pixel.js\").Pixel} pixel Pixel position in the map viewport.\n * @return {import(\"./coordinate.js\").Coordinate} The coordinate for the pixel position.\n * @api\n */\n PluggableMap.prototype.getCoordinateFromPixel = function (pixel) {\n return toUserCoordinate(this.getCoordinateFromPixelInternal(pixel), this.getView().getProjection());\n };\n /**\n * Get the coordinate for a given pixel. This returns a coordinate in the\n * map view projection.\n * @param {import(\"./pixel.js\").Pixel} pixel Pixel position in the map viewport.\n * @return {import(\"./coordinate.js\").Coordinate} The coordinate for the pixel position.\n */\n PluggableMap.prototype.getCoordinateFromPixelInternal = function (pixel) {\n var frameState = this.frameState_;\n if (!frameState) {\n return null;\n }\n else {\n return applyTransform(frameState.pixelToCoordinateTransform, pixel.slice());\n }\n };\n /**\n * Get the map controls. Modifying this collection changes the controls\n * associated with the map.\n * @return {Collection} Controls.\n * @api\n */\n PluggableMap.prototype.getControls = function () {\n return this.controls;\n };\n /**\n * Get the map overlays. Modifying this collection changes the overlays\n * associated with the map.\n * @return {Collection} Overlays.\n * @api\n */\n PluggableMap.prototype.getOverlays = function () {\n return this.overlays_;\n };\n /**\n * Get an overlay by its identifier (the value returned by overlay.getId()).\n * Note that the index treats string and numeric identifiers as the same. So\n * `map.getOverlayById(2)` will return an overlay with id `'2'` or `2`.\n * @param {string|number} id Overlay identifier.\n * @return {import(\"./Overlay.js\").default} Overlay.\n * @api\n */\n PluggableMap.prototype.getOverlayById = function (id) {\n var overlay = this.overlayIdIndex_[id.toString()];\n return overlay !== undefined ? overlay : null;\n };\n /**\n * Get the map interactions. Modifying this collection changes the interactions\n * associated with the map.\n *\n * Interactions are used for e.g. pan, zoom and rotate.\n * @return {Collection} Interactions.\n * @api\n */\n PluggableMap.prototype.getInteractions = function () {\n return this.interactions;\n };\n /**\n * Get the layergroup associated with this map.\n * @return {LayerGroup} A layer group containing the layers in this map.\n * @observable\n * @api\n */\n PluggableMap.prototype.getLayerGroup = function () {\n return (\n /** @type {LayerGroup} */ (this.get(MapProperty.LAYERGROUP)));\n };\n /**\n * Get the collection of layers associated with this map.\n * @return {!Collection} Layers.\n * @api\n */\n PluggableMap.prototype.getLayers = function () {\n var layers = this.getLayerGroup().getLayers();\n return layers;\n };\n /**\n * @return {boolean} Layers have sources that are still loading.\n */\n PluggableMap.prototype.getLoading = function () {\n var layerStatesArray = this.getLayerGroup().getLayerStatesArray();\n for (var i = 0, ii = layerStatesArray.length; i < ii; ++i) {\n var layer = layerStatesArray[i].layer;\n var source = /** @type {import(\"./layer/Layer.js\").default} */ (layer).getSource();\n if (source && source.loading) {\n return true;\n }\n }\n return false;\n };\n /**\n * Get the pixel for a coordinate. This takes a coordinate in the user\n * projection and returns the corresponding pixel.\n * @param {import(\"./coordinate.js\").Coordinate} coordinate A map coordinate.\n * @return {import(\"./pixel.js\").Pixel} A pixel position in the map viewport.\n * @api\n */\n PluggableMap.prototype.getPixelFromCoordinate = function (coordinate) {\n var viewCoordinate = fromUserCoordinate(coordinate, this.getView().getProjection());\n return this.getPixelFromCoordinateInternal(viewCoordinate);\n };\n /**\n * Get the pixel for a coordinate. This takes a coordinate in the map view\n * projection and returns the corresponding pixel.\n * @param {import(\"./coordinate.js\").Coordinate} coordinate A map coordinate.\n * @return {import(\"./pixel.js\").Pixel} A pixel position in the map viewport.\n */\n PluggableMap.prototype.getPixelFromCoordinateInternal = function (coordinate) {\n var frameState = this.frameState_;\n if (!frameState) {\n return null;\n }\n else {\n return applyTransform(frameState.coordinateToPixelTransform, coordinate.slice(0, 2));\n }\n };\n /**\n * Get the map renderer.\n * @return {import(\"./renderer/Map.js\").default} Renderer\n */\n PluggableMap.prototype.getRenderer = function () {\n return this.renderer_;\n };\n /**\n * Get the size of this map.\n * @return {import(\"./size.js\").Size|undefined} The size in pixels of the map in the DOM.\n * @observable\n * @api\n */\n PluggableMap.prototype.getSize = function () {\n return (\n /** @type {import(\"./size.js\").Size|undefined} */ (this.get(MapProperty.SIZE)));\n };\n /**\n * Get the view associated with this map. A view manages properties such as\n * center and resolution.\n * @return {View} The view that controls this map.\n * @observable\n * @api\n */\n PluggableMap.prototype.getView = function () {\n return (\n /** @type {View} */ (this.get(MapProperty.VIEW)));\n };\n /**\n * Get the element that serves as the map viewport.\n * @return {HTMLElement} Viewport.\n * @api\n */\n PluggableMap.prototype.getViewport = function () {\n return this.viewport_;\n };\n /**\n * Get the element that serves as the container for overlays. Elements added to\n * this container will let mousedown and touchstart events through to the map,\n * so clicks and gestures on an overlay will trigger {@link module:ol/MapBrowserEvent~MapBrowserEvent}\n * events.\n * @return {!HTMLElement} The map's overlay container.\n */\n PluggableMap.prototype.getOverlayContainer = function () {\n return this.overlayContainer_;\n };\n /**\n * Get the element that serves as a container for overlays that don't allow\n * event propagation. Elements added to this container won't let mousedown and\n * touchstart events through to the map, so clicks and gestures on an overlay\n * don't trigger any {@link module:ol/MapBrowserEvent~MapBrowserEvent}.\n * @return {!HTMLElement} The map's overlay container that stops events.\n */\n PluggableMap.prototype.getOverlayContainerStopEvent = function () {\n return this.overlayContainerStopEvent_;\n };\n /**\n * @param {import(\"./Tile.js\").default} tile Tile.\n * @param {string} tileSourceKey Tile source key.\n * @param {import(\"./coordinate.js\").Coordinate} tileCenter Tile center.\n * @param {number} tileResolution Tile resolution.\n * @return {number} Tile priority.\n */\n PluggableMap.prototype.getTilePriority = function (tile, tileSourceKey, tileCenter, tileResolution) {\n // Filter out tiles at higher zoom levels than the current zoom level, or that\n // are outside the visible extent.\n var frameState = this.frameState_;\n if (!frameState || !(tileSourceKey in frameState.wantedTiles)) {\n return DROP;\n }\n if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) {\n return DROP;\n }\n // Prioritize the highest zoom level tiles closest to the focus.\n // Tiles at higher zoom levels are prioritized using Math.log(tileResolution).\n // Within a zoom level, tiles are prioritized by the distance in pixels between\n // the center of the tile and the center of the viewport. The factor of 65536\n // means that the prioritization should behave as desired for tiles up to\n // 65536 * Math.log(2) = 45426 pixels from the focus.\n var center = frameState.viewState.center;\n var deltaX = tileCenter[0] - center[0];\n var deltaY = tileCenter[1] - center[1];\n return 65536 * Math.log(tileResolution) +\n Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;\n };\n /**\n * @param {Event} browserEvent Browser event.\n * @param {string=} opt_type Type.\n */\n PluggableMap.prototype.handleBrowserEvent = function (browserEvent, opt_type) {\n var type = opt_type || browserEvent.type;\n var mapBrowserEvent = new MapBrowserEvent(type, this, browserEvent);\n this.handleMapBrowserEvent(mapBrowserEvent);\n };\n /**\n * @param {MapBrowserEvent} mapBrowserEvent The event to handle.\n */\n PluggableMap.prototype.handleMapBrowserEvent = function (mapBrowserEvent) {\n if (!this.frameState_) {\n // With no view defined, we cannot translate pixels into geographical\n // coordinates so interactions cannot be used.\n return;\n }\n var target = mapBrowserEvent.originalEvent.target;\n while (target instanceof HTMLElement) {\n if (target.parentElement === this.overlayContainerStopEvent_) {\n return;\n }\n target = target.parentElement;\n }\n mapBrowserEvent.frameState = this.frameState_;\n var interactionsArray = this.getInteractions().getArray();\n if (this.dispatchEvent(mapBrowserEvent) !== false) {\n for (var i = interactionsArray.length - 1; i >= 0; i--) {\n var interaction = interactionsArray[i];\n if (!interaction.getActive()) {\n continue;\n }\n var cont = interaction.handleEvent(mapBrowserEvent);\n if (!cont) {\n break;\n }\n }\n }\n };\n /**\n * @protected\n */\n PluggableMap.prototype.handlePostRender = function () {\n var frameState = this.frameState_;\n // Manage the tile queue\n // Image loads are expensive and a limited resource, so try to use them\n // efficiently:\n // * When the view is static we allow a large number of parallel tile loads\n // to complete the frame as quickly as possible.\n // * When animating or interacting, image loads can cause janks, so we reduce\n // the maximum number of loads per frame and limit the number of parallel\n // tile loads to remain reactive to view changes and to reduce the chance of\n // loading tiles that will quickly disappear from view.\n var tileQueue = this.tileQueue_;\n if (!tileQueue.isEmpty()) {\n var maxTotalLoading = this.maxTilesLoading_;\n var maxNewLoads = maxTotalLoading;\n if (frameState) {\n var hints = frameState.viewHints;\n if (hints[ViewHint.ANIMATING] || hints[ViewHint.INTERACTING]) {\n var lowOnFrameBudget = !IMAGE_DECODE && Date.now() - frameState.time > 8;\n maxTotalLoading = lowOnFrameBudget ? 0 : 8;\n maxNewLoads = lowOnFrameBudget ? 0 : 2;\n }\n }\n if (tileQueue.getTilesLoading() < maxTotalLoading) {\n tileQueue.reprioritize(); // FIXME only call if view has changed\n tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads);\n }\n }\n if (frameState && this.hasListener(RenderEventType.RENDERCOMPLETE) && !frameState.animate &&\n !this.tileQueue_.getTilesLoading() && !this.getLoading()) {\n this.renderer_.dispatchRenderEvent(RenderEventType.RENDERCOMPLETE, frameState);\n }\n var postRenderFunctions = this.postRenderFunctions_;\n for (var i = 0, ii = postRenderFunctions.length; i < ii; ++i) {\n postRenderFunctions[i](this, frameState);\n }\n postRenderFunctions.length = 0;\n };\n /**\n * @private\n */\n PluggableMap.prototype.handleSizeChanged_ = function () {\n if (this.getView()) {\n this.getView().resolveConstraints(0);\n }\n this.render();\n };\n /**\n * @private\n */\n PluggableMap.prototype.handleTargetChanged_ = function () {\n // target may be undefined, null, a string or an Element.\n // If it's a string we convert it to an Element before proceeding.\n // If it's not now an Element we remove the viewport from the DOM.\n // If it's an Element we append the viewport element to it.\n var targetElement;\n if (this.getTarget()) {\n targetElement = this.getTargetElement();\n }\n if (this.focusHandlerKeys_) {\n for (var i = 0, ii = this.focusHandlerKeys_.length; i < ii; ++i) {\n unlistenByKey(this.focusHandlerKeys_[i]);\n }\n this.focusHandlerKeys_ = null;\n }\n if (this.keyHandlerKeys_) {\n for (var i = 0, ii = this.keyHandlerKeys_.length; i < ii; ++i) {\n unlistenByKey(this.keyHandlerKeys_[i]);\n }\n this.keyHandlerKeys_ = null;\n }\n if (!targetElement) {\n if (this.renderer_) {\n clearTimeout(this.postRenderTimeoutHandle_);\n this.postRenderFunctions_.length = 0;\n this.renderer_.dispose();\n this.renderer_ = null;\n }\n if (this.animationDelayKey_) {\n cancelAnimationFrame(this.animationDelayKey_);\n this.animationDelayKey_ = undefined;\n }\n removeNode(this.viewport_);\n if (this.handleResize_ !== undefined) {\n removeEventListener(EventType.RESIZE, this.handleResize_, false);\n this.handleResize_ = undefined;\n }\n }\n else {\n targetElement.appendChild(this.viewport_);\n if (!this.renderer_) {\n this.renderer_ = this.createRenderer();\n }\n var hasFocus = true;\n if (targetElement.hasAttribute('tabindex')) {\n hasFocus = document.activeElement === targetElement;\n this.focusHandlerKeys_ = [\n listen(targetElement, EventType.FOCUS, setTouchAction.bind(this, this.viewport_, 'none')),\n listen(targetElement, EventType.BLUR, setTouchAction.bind(this, this.viewport_, 'auto'))\n ];\n }\n setTouchAction(this.viewport_, hasFocus ? 'none' : 'auto');\n var keyboardEventTarget = !this.keyboardEventTarget_ ?\n targetElement : this.keyboardEventTarget_;\n this.keyHandlerKeys_ = [\n listen(keyboardEventTarget, EventType.KEYDOWN, this.handleBrowserEvent, this),\n listen(keyboardEventTarget, EventType.KEYPRESS, this.handleBrowserEvent, this)\n ];\n if (!this.handleResize_) {\n this.handleResize_ = this.updateSize.bind(this);\n window.addEventListener(EventType.RESIZE, this.handleResize_, false);\n }\n }\n this.updateSize();\n // updateSize calls setSize, so no need to call this.render\n // ourselves here.\n };\n /**\n * @private\n */\n PluggableMap.prototype.handleTileChange_ = function () {\n this.render();\n };\n /**\n * @private\n */\n PluggableMap.prototype.handleViewPropertyChanged_ = function () {\n this.render();\n };\n /**\n * @private\n */\n PluggableMap.prototype.handleViewChanged_ = function () {\n if (this.viewPropertyListenerKey_) {\n unlistenByKey(this.viewPropertyListenerKey_);\n this.viewPropertyListenerKey_ = null;\n }\n if (this.viewChangeListenerKey_) {\n unlistenByKey(this.viewChangeListenerKey_);\n this.viewChangeListenerKey_ = null;\n }\n var view = this.getView();\n if (view) {\n this.viewport_.setAttribute('data-view', getUid(view));\n this.viewPropertyListenerKey_ = listen(view, ObjectEventType.PROPERTYCHANGE, this.handleViewPropertyChanged_, this);\n this.viewChangeListenerKey_ = listen(view, EventType.CHANGE, this.handleViewPropertyChanged_, this);\n view.resolveConstraints(0);\n }\n this.render();\n };\n /**\n * @private\n */\n PluggableMap.prototype.handleLayerGroupChanged_ = function () {\n if (this.layerGroupPropertyListenerKeys_) {\n this.layerGroupPropertyListenerKeys_.forEach(unlistenByKey);\n this.layerGroupPropertyListenerKeys_ = null;\n }\n var layerGroup = this.getLayerGroup();\n if (layerGroup) {\n this.layerGroupPropertyListenerKeys_ = [\n listen(layerGroup, ObjectEventType.PROPERTYCHANGE, this.render, this),\n listen(layerGroup, EventType.CHANGE, this.render, this)\n ];\n }\n this.render();\n };\n /**\n * @return {boolean} Is rendered.\n */\n PluggableMap.prototype.isRendered = function () {\n return !!this.frameState_;\n };\n /**\n * Requests an immediate render in a synchronous manner.\n * @api\n */\n PluggableMap.prototype.renderSync = function () {\n if (this.animationDelayKey_) {\n cancelAnimationFrame(this.animationDelayKey_);\n }\n this.animationDelay_();\n };\n /**\n * Redraws all text after new fonts have loaded\n */\n PluggableMap.prototype.redrawText = function () {\n var layerStates = this.getLayerGroup().getLayerStatesArray();\n for (var i = 0, ii = layerStates.length; i < ii; ++i) {\n var layer = layerStates[i].layer;\n if (layer.hasRenderer()) {\n layer.getRenderer().handleFontsChanged();\n }\n }\n };\n /**\n * Request a map rendering (at the next animation frame).\n * @api\n */\n PluggableMap.prototype.render = function () {\n if (this.renderer_ && this.animationDelayKey_ === undefined) {\n this.animationDelayKey_ = requestAnimationFrame(this.animationDelay_);\n }\n };\n /**\n * Remove the given control from the map.\n * @param {import(\"./control/Control.js\").default} control Control.\n * @return {import(\"./control/Control.js\").default|undefined} The removed control (or undefined\n * if the control was not found).\n * @api\n */\n PluggableMap.prototype.removeControl = function (control) {\n return this.getControls().remove(control);\n };\n /**\n * Remove the given interaction from the map.\n * @param {import(\"./interaction/Interaction.js\").default} interaction Interaction to remove.\n * @return {import(\"./interaction/Interaction.js\").default|undefined} The removed interaction (or\n * undefined if the interaction was not found).\n * @api\n */\n PluggableMap.prototype.removeInteraction = function (interaction) {\n return this.getInteractions().remove(interaction);\n };\n /**\n * Removes the given layer from the map.\n * @param {import(\"./layer/Base.js\").default} layer Layer.\n * @return {import(\"./layer/Base.js\").default|undefined} The removed layer (or undefined if the\n * layer was not found).\n * @api\n */\n PluggableMap.prototype.removeLayer = function (layer) {\n var layers = this.getLayerGroup().getLayers();\n return layers.remove(layer);\n };\n /**\n * Remove the given overlay from the map.\n * @param {import(\"./Overlay.js\").default} overlay Overlay.\n * @return {import(\"./Overlay.js\").default|undefined} The removed overlay (or undefined\n * if the overlay was not found).\n * @api\n */\n PluggableMap.prototype.removeOverlay = function (overlay) {\n return this.getOverlays().remove(overlay);\n };\n /**\n * @param {number} time Time.\n * @private\n */\n PluggableMap.prototype.renderFrame_ = function (time) {\n var size = this.getSize();\n var view = this.getView();\n var previousFrameState = this.frameState_;\n /** @type {?FrameState} */\n var frameState = null;\n if (size !== undefined && hasArea(size) && view && view.isDef()) {\n var viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined);\n var viewState = view.getState();\n frameState = {\n animate: false,\n coordinateToPixelTransform: this.coordinateToPixelTransform_,\n declutterItems: previousFrameState ? previousFrameState.declutterItems : [],\n extent: getForViewAndSize(viewState.center, viewState.resolution, viewState.rotation, size),\n index: this.frameIndex_++,\n layerIndex: 0,\n layerStatesArray: this.getLayerGroup().getLayerStatesArray(),\n pixelRatio: this.pixelRatio_,\n pixelToCoordinateTransform: this.pixelToCoordinateTransform_,\n postRenderFunctions: [],\n size: size,\n tileQueue: this.tileQueue_,\n time: time,\n usedTiles: {},\n viewState: viewState,\n viewHints: viewHints,\n wantedTiles: {}\n };\n }\n this.frameState_ = frameState;\n this.renderer_.renderFrame(frameState);\n if (frameState) {\n if (frameState.animate) {\n this.render();\n }\n Array.prototype.push.apply(this.postRenderFunctions_, frameState.postRenderFunctions);\n if (previousFrameState) {\n var moveStart = !this.previousExtent_ ||\n (!isEmpty(this.previousExtent_) &&\n !equals(frameState.extent, this.previousExtent_));\n if (moveStart) {\n this.dispatchEvent(new MapEvent(MapEventType.MOVESTART, this, previousFrameState));\n this.previousExtent_ = createOrUpdateEmpty(this.previousExtent_);\n }\n }\n var idle = this.previousExtent_ &&\n !frameState.viewHints[ViewHint.ANIMATING] &&\n !frameState.viewHints[ViewHint.INTERACTING] &&\n !equals(frameState.extent, this.previousExtent_);\n if (idle) {\n this.dispatchEvent(new MapEvent(MapEventType.MOVEEND, this, frameState));\n clone(frameState.extent, this.previousExtent_);\n }\n }\n this.dispatchEvent(new MapEvent(MapEventType.POSTRENDER, this, frameState));\n this.postRenderTimeoutHandle_ = setTimeout(this.handlePostRender.bind(this), 0);\n };\n /**\n * Sets the layergroup of this map.\n * @param {LayerGroup} layerGroup A layer group containing the layers in this map.\n * @observable\n * @api\n */\n PluggableMap.prototype.setLayerGroup = function (layerGroup) {\n this.set(MapProperty.LAYERGROUP, layerGroup);\n };\n /**\n * Set the size of this map.\n * @param {import(\"./size.js\").Size|undefined} size The size in pixels of the map in the DOM.\n * @observable\n * @api\n */\n PluggableMap.prototype.setSize = function (size) {\n this.set(MapProperty.SIZE, size);\n };\n /**\n * Set the target element to render this map into.\n * @param {HTMLElement|string|undefined} target The Element or id of the Element\n * that the map is rendered in.\n * @observable\n * @api\n */\n PluggableMap.prototype.setTarget = function (target) {\n this.set(MapProperty.TARGET, target);\n };\n /**\n * Set the view for this map.\n * @param {View} view The view that controls this map.\n * @observable\n * @api\n */\n PluggableMap.prototype.setView = function (view) {\n this.set(MapProperty.VIEW, view);\n };\n /**\n * Force a recalculation of the map viewport size. This should be called when\n * third-party code changes the size of the map viewport.\n * @api\n */\n PluggableMap.prototype.updateSize = function () {\n var targetElement = this.getTargetElement();\n if (!targetElement) {\n this.setSize(undefined);\n }\n else {\n var computedStyle = getComputedStyle(targetElement);\n this.setSize([\n targetElement.offsetWidth -\n parseFloat(computedStyle['borderLeftWidth']) -\n parseFloat(computedStyle['paddingLeft']) -\n parseFloat(computedStyle['paddingRight']) -\n parseFloat(computedStyle['borderRightWidth']),\n targetElement.offsetHeight -\n parseFloat(computedStyle['borderTopWidth']) -\n parseFloat(computedStyle['paddingTop']) -\n parseFloat(computedStyle['paddingBottom']) -\n parseFloat(computedStyle['borderBottomWidth'])\n ]);\n }\n };\n return PluggableMap;\n}(BaseObject));\n/**\n * @param {MapOptions} options Map options.\n * @return {MapOptionsInternal} Internal map options.\n */\nfunction createOptionsInternal(options) {\n /**\n * @type {HTMLElement|Document}\n */\n var keyboardEventTarget = null;\n if (options.keyboardEventTarget !== undefined) {\n keyboardEventTarget = typeof options.keyboardEventTarget === 'string' ?\n document.getElementById(options.keyboardEventTarget) :\n options.keyboardEventTarget;\n }\n /**\n * @type {Object}\n */\n var values = {};\n var layerGroup = options.layers && typeof /** @type {?} */ (options.layers).getLayers === 'function' ?\n /** @type {LayerGroup} */ (options.layers) : new LayerGroup({ layers: /** @type {Collection} */ (options.layers) });\n values[MapProperty.LAYERGROUP] = layerGroup;\n values[MapProperty.TARGET] = options.target;\n values[MapProperty.VIEW] = options.view !== undefined ?\n options.view : new View();\n var controls;\n if (options.controls !== undefined) {\n if (Array.isArray(options.controls)) {\n controls = new Collection(options.controls.slice());\n }\n else {\n assert(typeof /** @type {?} */ (options.controls).getArray === 'function', 47); // Expected `controls` to be an array or an `import(\"./Collection.js\").Collection`\n controls = /** @type {Collection} */ (options.controls);\n }\n }\n var interactions;\n if (options.interactions !== undefined) {\n if (Array.isArray(options.interactions)) {\n interactions = new Collection(options.interactions.slice());\n }\n else {\n assert(typeof /** @type {?} */ (options.interactions).getArray === 'function', 48); // Expected `interactions` to be an array or an `import(\"./Collection.js\").Collection`\n interactions = /** @type {Collection} */ (options.interactions);\n }\n }\n var overlays;\n if (options.overlays !== undefined) {\n if (Array.isArray(options.overlays)) {\n overlays = new Collection(options.overlays.slice());\n }\n else {\n assert(typeof /** @type {?} */ (options.overlays).getArray === 'function', 49); // Expected `overlays` to be an array or an `import(\"./Collection.js\").Collection`\n overlays = options.overlays;\n }\n }\n else {\n overlays = new Collection();\n }\n return {\n controls: controls,\n interactions: interactions,\n keyboardEventTarget: keyboardEventTarget,\n overlays: overlays,\n values: values\n };\n}\nexport default PluggableMap;\n//# sourceMappingURL=PluggableMap.js.map","/**\n * @module ol/css\n */\n/**\n * @typedef {Object} FontParameters\n * @property {Array} families\n * @property {string} style\n * @property {string} weight\n */\n/**\n * The CSS class for hidden feature.\n *\n * @const\n * @type {string}\n */\nexport var CLASS_HIDDEN = 'ol-hidden';\n/**\n * The CSS class that we'll give the DOM elements to have them selectable.\n *\n * @const\n * @type {string}\n */\nexport var CLASS_SELECTABLE = 'ol-selectable';\n/**\n * The CSS class that we'll give the DOM elements to have them unselectable.\n *\n * @const\n * @type {string}\n */\nexport var CLASS_UNSELECTABLE = 'ol-unselectable';\n/**\n * The CSS class for unsupported feature.\n *\n * @const\n * @type {string}\n */\nexport var CLASS_UNSUPPORTED = 'ol-unsupported';\n/**\n * The CSS class for controls.\n *\n * @const\n * @type {string}\n */\nexport var CLASS_CONTROL = 'ol-control';\n/**\n * The CSS class that we'll give the DOM elements that are collapsed, i.e.\n * to those elements which usually can be expanded.\n *\n * @const\n * @type {string}\n */\nexport var CLASS_COLLAPSED = 'ol-collapsed';\n/**\n * Get the list of font families from a font spec. Note that this doesn't work\n * for font families that have commas in them.\n * @param {string} The CSS font property.\n * @return {FontParameters} The font families (or null if the input spec is invalid).\n */\nexport var getFontParameters = (function () {\n /**\n * @type {CSSStyleDeclaration}\n */\n var style;\n /**\n * @type {Object}\n */\n var cache = {};\n return function (font) {\n if (!style) {\n style = document.createElement('div').style;\n }\n if (!(font in cache)) {\n style.font = font;\n var family = style.fontFamily;\n var fontWeight = style.fontWeight;\n var fontStyle = style.fontStyle;\n style.font = '';\n if (!family) {\n return null;\n }\n var families = family.split(/,\\s?/);\n cache[font] = {\n families: families,\n weight: fontWeight,\n style: fontStyle\n };\n }\n return cache[font];\n };\n})();\n//# sourceMappingURL=css.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/control/Control\n */\nimport { VOID } from '../functions.js';\nimport MapEventType from '../MapEventType.js';\nimport BaseObject from '../Object.js';\nimport { removeNode } from '../dom.js';\nimport { listen, unlistenByKey } from '../events.js';\n/**\n * @typedef {Object} Options\n * @property {HTMLElement} [element] The element is the control's\n * container element. This only needs to be specified if you're developing\n * a custom control.\n * @property {function(import(\"../MapEvent.js\").default)} [render] Function called when\n * the control should be re-rendered. This is called in a `requestAnimationFrame`\n * callback.\n * @property {HTMLElement|string} [target] Specify a target if you want\n * the control to be rendered outside of the map's viewport.\n */\n/**\n * @classdesc\n * A control is a visible widget with a DOM element in a fixed position on the\n * screen. They can involve user input (buttons), or be informational only;\n * the position is determined using CSS. By default these are placed in the\n * container with CSS class name `ol-overlaycontainer-stopevent`, but can use\n * any outside DOM element.\n *\n * This is the base class for controls. You can use it for simple custom\n * controls by creating the element with listeners, creating an instance:\n * ```js\n * var myControl = new Control({element: myElement});\n * ```\n * and then adding this to the map.\n *\n * The main advantage of having this as a control rather than a simple separate\n * DOM element is that preventing propagation is handled for you. Controls\n * will also be objects in a {@link module:ol/Collection~Collection}, so you can use their methods.\n *\n * You can also extend this base for your own control class. See\n * examples/custom-controls for an example of how to do this.\n *\n * @api\n */\nvar Control = /** @class */ (function (_super) {\n __extends(Control, _super);\n /**\n * @param {Options} options Control options.\n */\n function Control(options) {\n var _this = _super.call(this) || this;\n /**\n * @protected\n * @type {HTMLElement}\n */\n _this.element = options.element ? options.element : null;\n /**\n * @private\n * @type {HTMLElement}\n */\n _this.target_ = null;\n /**\n * @private\n * @type {import(\"../PluggableMap.js\").default}\n */\n _this.map_ = null;\n /**\n * @protected\n * @type {!Array}\n */\n _this.listenerKeys = [];\n /**\n * @type {function(import(\"../MapEvent.js\").default): void}\n */\n _this.render = options.render ? options.render : VOID;\n if (options.target) {\n _this.setTarget(options.target);\n }\n return _this;\n }\n /**\n * @inheritDoc\n */\n Control.prototype.disposeInternal = function () {\n removeNode(this.element);\n _super.prototype.disposeInternal.call(this);\n };\n /**\n * Get the map associated with this control.\n * @return {import(\"../PluggableMap.js\").default} Map.\n * @api\n */\n Control.prototype.getMap = function () {\n return this.map_;\n };\n /**\n * Remove the control from its current map and attach it to the new map.\n * Subclasses may set up event handlers to get notified about changes to\n * the map here.\n * @param {import(\"../PluggableMap.js\").default} map Map.\n * @api\n */\n Control.prototype.setMap = function (map) {\n if (this.map_) {\n removeNode(this.element);\n }\n for (var i = 0, ii = this.listenerKeys.length; i < ii; ++i) {\n unlistenByKey(this.listenerKeys[i]);\n }\n this.listenerKeys.length = 0;\n this.map_ = map;\n if (this.map_) {\n var target = this.target_ ?\n this.target_ : map.getOverlayContainerStopEvent();\n target.appendChild(this.element);\n if (this.render !== VOID) {\n this.listenerKeys.push(listen(map, MapEventType.POSTRENDER, this.render, this));\n }\n map.render();\n }\n };\n /**\n * This function is used to set a target element for the control. It has no\n * effect if it is called after the control has been added to the map (i.e.\n * after `setMap` is called on the control). If no `target` is set in the\n * options passed to the control constructor and if `setTarget` is not called\n * then the control is added to the map's overlay container.\n * @param {HTMLElement|string} target Target.\n * @api\n */\n Control.prototype.setTarget = function (target) {\n this.target_ = typeof target === 'string' ?\n document.getElementById(target) :\n target;\n };\n return Control;\n}(BaseObject));\nexport default Control;\n//# sourceMappingURL=Control.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/layer/Layer\n */\nimport { listen, unlistenByKey } from '../events.js';\nimport EventType from '../events/EventType.js';\nimport { getChangeEventType } from '../Object.js';\nimport BaseLayer from './Base.js';\nimport LayerProperty from './Property.js';\nimport { assign } from '../obj.js';\nimport RenderEventType from '../render/EventType.js';\nimport SourceState from '../source/State.js';\n/**\n * @typedef {function(import(\"../PluggableMap.js\").FrameState):HTMLElement} RenderFunction\n */\n/**\n * @typedef {Object} Options\n * @property {string} [className='ol-layer'] A CSS class name to set to the layer element.\n * @property {number} [opacity=1] Opacity (0, 1).\n * @property {boolean} [visible=true] Visibility.\n * @property {import(\"../extent.js\").Extent} [extent] The bounding extent for layer rendering. The layer will not be\n * rendered outside of this extent.\n * @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers\n * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed\n * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()`\n * method was used.\n * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be\n * visible.\n * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will\n * be visible.\n * @property {import(\"../source/Source.js\").default} [source] Source for this layer. If not provided to the constructor,\n * the source can be set by calling {@link module:ol/layer/Layer#setSource layer.setSource(source)} after\n * construction.\n * @property {import(\"../PluggableMap.js\").default} [map] Map.\n * @property {RenderFunction} [render] Render function. Takes the frame state as input and is expected to return an\n * HTML element. Will overwrite the default rendering for the layer.\n */\n/**\n * @typedef {Object} State\n * @property {import(\"./Base.js\").default} layer\n * @property {number} opacity Opacity, the value is rounded to two digits to appear after the decimal point.\n * @property {SourceState} sourceState\n * @property {boolean} visible\n * @property {boolean} managed\n * @property {import(\"../extent.js\").Extent} [extent]\n * @property {number} zIndex\n * @property {number} maxResolution\n * @property {number} minResolution\n * @property {number} minZoom\n * @property {number} maxZoom\n */\n/**\n * @classdesc\n * Base class from which all layer types are derived. This should only be instantiated\n * in the case where a custom layer is be added to the map with a custom `render` function.\n * Such a function can be specified in the `options` object, and is expected to return an HTML element.\n *\n * A visual representation of raster or vector map data.\n * Layers group together those properties that pertain to how the data is to be\n * displayed, irrespective of the source of that data.\n *\n * Layers are usually added to a map with {@link module:ol/Map#addLayer}. Components\n * like {@link module:ol/interaction/Select~Select} use unmanaged layers\n * internally. These unmanaged layers are associated with the map using\n * {@link module:ol/layer/Layer~Layer#setMap} instead.\n *\n * A generic `change` event is fired when the state of the source changes.\n *\n * Please note that for performance reasons several layers might get rendered to\n * the same HTML element, which will cause {@link module:ol/Map~Map#forEachLayerAtPixel} to\n * give false positives. To avoid this, apply different `className` properties to the\n * layers at creation time.\n *\n * @fires import(\"../render/Event.js\").RenderEvent#prerender\n * @fires import(\"../render/Event.js\").RenderEvent#postrender\n *\n * @template {import(\"../source/Source.js\").default} SourceType\n * @api\n */\nvar Layer = /** @class */ (function (_super) {\n __extends(Layer, _super);\n /**\n * @param {Options} options Layer options.\n */\n function Layer(options) {\n var _this = this;\n var baseOptions = assign({}, options);\n delete baseOptions.source;\n _this = _super.call(this, baseOptions) || this;\n /**\n * @private\n * @type {?import(\"../events.js\").EventsKey}\n */\n _this.mapPrecomposeKey_ = null;\n /**\n * @private\n * @type {?import(\"../events.js\").EventsKey}\n */\n _this.mapRenderKey_ = null;\n /**\n * @private\n * @type {?import(\"../events.js\").EventsKey}\n */\n _this.sourceChangeKey_ = null;\n /**\n * @private\n * @type {import(\"../renderer/Layer.js\").default}\n */\n _this.renderer_ = null;\n // Overwrite default render method with a custom one\n if (options.render) {\n _this.render = options.render;\n }\n if (options.map) {\n _this.setMap(options.map);\n }\n _this.addEventListener(getChangeEventType(LayerProperty.SOURCE), _this.handleSourcePropertyChange_);\n var source = options.source ? /** @type {SourceType} */ (options.source) : null;\n _this.setSource(source);\n return _this;\n }\n /**\n * @inheritDoc\n */\n Layer.prototype.getLayersArray = function (opt_array) {\n var array = opt_array ? opt_array : [];\n array.push(this);\n return array;\n };\n /**\n * @inheritDoc\n */\n Layer.prototype.getLayerStatesArray = function (opt_states) {\n var states = opt_states ? opt_states : [];\n states.push(this.getLayerState());\n return states;\n };\n /**\n * Get the layer source.\n * @return {SourceType} The layer source (or `null` if not yet set).\n * @observable\n * @api\n */\n Layer.prototype.getSource = function () {\n return /** @type {SourceType} */ (this.get(LayerProperty.SOURCE)) || null;\n };\n /**\n * @inheritDoc\n */\n Layer.prototype.getSourceState = function () {\n var source = this.getSource();\n return !source ? SourceState.UNDEFINED : source.getState();\n };\n /**\n * @private\n */\n Layer.prototype.handleSourceChange_ = function () {\n this.changed();\n };\n /**\n * @private\n */\n Layer.prototype.handleSourcePropertyChange_ = function () {\n if (this.sourceChangeKey_) {\n unlistenByKey(this.sourceChangeKey_);\n this.sourceChangeKey_ = null;\n }\n var source = this.getSource();\n if (source) {\n this.sourceChangeKey_ = listen(source, EventType.CHANGE, this.handleSourceChange_, this);\n }\n this.changed();\n };\n /**\n * @param {import(\"../pixel\").Pixel} pixel Pixel.\n * @return {Promise>} Promise that resolves with\n * an array of features.\n */\n Layer.prototype.getFeatures = function (pixel) {\n return this.renderer_.getFeatures(pixel);\n };\n /**\n * In charge to manage the rendering of the layer. One layer type is\n * bounded with one layer renderer.\n * @param {?import(\"../PluggableMap.js\").FrameState} frameState Frame state.\n * @param {HTMLElement} target Target which the renderer may (but need not) use\n * for rendering its content.\n * @return {HTMLElement} The rendered element.\n */\n Layer.prototype.render = function (frameState, target) {\n var layerRenderer = this.getRenderer();\n if (layerRenderer.prepareFrame(frameState)) {\n return layerRenderer.renderFrame(frameState, target);\n }\n };\n /**\n * Sets the layer to be rendered on top of other layers on a map. The map will\n * not manage this layer in its layers collection, and the callback in\n * {@link module:ol/Map#forEachLayerAtPixel} will receive `null` as layer. This\n * is useful for temporary layers. To remove an unmanaged layer from the map,\n * use `#setMap(null)`.\n *\n * To add the layer to a map and have it managed by the map, use\n * {@link module:ol/Map#addLayer} instead.\n * @param {import(\"../PluggableMap.js\").default} map Map.\n * @api\n */\n Layer.prototype.setMap = function (map) {\n if (this.mapPrecomposeKey_) {\n unlistenByKey(this.mapPrecomposeKey_);\n this.mapPrecomposeKey_ = null;\n }\n if (!map) {\n this.changed();\n }\n if (this.mapRenderKey_) {\n unlistenByKey(this.mapRenderKey_);\n this.mapRenderKey_ = null;\n }\n if (map) {\n this.mapPrecomposeKey_ = listen(map, RenderEventType.PRECOMPOSE, function (evt) {\n var renderEvent = /** @type {import(\"../render/Event.js\").default} */ (evt);\n renderEvent.frameState.layerStatesArray.push(this.getLayerState(false));\n }, this);\n this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map);\n this.changed();\n }\n };\n /**\n * Set the layer source.\n * @param {SourceType} source The layer source.\n * @observable\n * @api\n */\n Layer.prototype.setSource = function (source) {\n this.set(LayerProperty.SOURCE, source);\n };\n /**\n * Get the renderer for this layer.\n * @return {import(\"../renderer/Layer.js\").default} The layer renderer.\n */\n Layer.prototype.getRenderer = function () {\n if (!this.renderer_) {\n this.renderer_ = this.createRenderer();\n }\n return this.renderer_;\n };\n /**\n * @return {boolean} The layer has a renderer.\n */\n Layer.prototype.hasRenderer = function () {\n return !!this.renderer_;\n };\n /**\n * Create a renderer for this layer.\n * @return {import(\"../renderer/Layer.js\").default} A layer renderer.\n * @protected\n */\n Layer.prototype.createRenderer = function () {\n return null;\n };\n /**\n * @inheritDoc\n */\n Layer.prototype.disposeInternal = function () {\n this.setSource(null);\n _super.prototype.disposeInternal.call(this);\n };\n return Layer;\n}(BaseLayer));\n/**\n * Return `true` if the layer is visible and if the provided view state\n * has resolution and zoom levels that are in range of the layer's min/max.\n * @param {State} layerState Layer state.\n * @param {import(\"../View.js\").State} viewState View state.\n * @return {boolean} The layer is visible at the given view state.\n */\nexport function inView(layerState, viewState) {\n if (!layerState.visible) {\n return false;\n }\n var resolution = viewState.resolution;\n if (resolution < layerState.minResolution || resolution >= layerState.maxResolution) {\n return false;\n }\n var zoom = viewState.zoom;\n return zoom > layerState.minZoom && zoom <= layerState.maxZoom;\n}\nexport default Layer;\n//# sourceMappingURL=Layer.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/control/Attribution\n */\nimport { equals } from '../array.js';\nimport Control from './Control.js';\nimport { CLASS_CONTROL, CLASS_UNSELECTABLE, CLASS_COLLAPSED } from '../css.js';\nimport { removeChildren, replaceNode } from '../dom.js';\nimport EventType from '../events/EventType.js';\nimport { inView } from '../layer/Layer.js';\n/**\n * @typedef {Object} Options\n * @property {string} [className='ol-attribution'] CSS class name.\n * @property {HTMLElement|string} [target] Specify a target if you\n * want the control to be rendered outside of the map's\n * viewport.\n * @property {boolean} [collapsible] Specify if attributions can\n * be collapsed. If not specified, sources control this behavior with their\n * `attributionsCollapsible` setting.\n * @property {boolean} [collapsed=true] Specify if attributions should\n * be collapsed at startup.\n * @property {string} [tipLabel='Attributions'] Text label to use for the button tip.\n * @property {string} [label='i'] Text label to use for the\n * collapsed attributions button.\n * Instead of text, also an element (e.g. a `span` element) can be used.\n * @property {string|HTMLElement} [collapseLabel='»'] Text label to use\n * for the expanded attributions button.\n * Instead of text, also an element (e.g. a `span` element) can be used.\n * @property {function(import(\"../MapEvent.js\").default)} [render] Function called when\n * the control should be re-rendered. This is called in a `requestAnimationFrame`\n * callback.\n */\n/**\n * @classdesc\n * Control to show all the attributions associated with the layer sources\n * in the map. This control is one of the default controls included in maps.\n * By default it will show in the bottom right portion of the map, but this can\n * be changed by using a css selector for `.ol-attribution`.\n *\n * @api\n */\nvar Attribution = /** @class */ (function (_super) {\n __extends(Attribution, _super);\n /**\n * @param {Options=} opt_options Attribution options.\n */\n function Attribution(opt_options) {\n var _this = this;\n var options = opt_options ? opt_options : {};\n _this = _super.call(this, {\n element: document.createElement('div'),\n render: options.render || render,\n target: options.target\n }) || this;\n /**\n * @private\n * @type {HTMLElement}\n */\n _this.ulElement_ = document.createElement('ul');\n /**\n * @private\n * @type {boolean}\n */\n _this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true;\n /**\n * @private\n * @type {boolean}\n */\n _this.overrideCollapsible_ = options.collapsible !== undefined;\n /**\n * @private\n * @type {boolean}\n */\n _this.collapsible_ = options.collapsible !== undefined ?\n options.collapsible : true;\n if (!_this.collapsible_) {\n _this.collapsed_ = false;\n }\n var className = options.className !== undefined ? options.className : 'ol-attribution';\n var tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Attributions';\n var collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\\u00BB';\n if (typeof collapseLabel === 'string') {\n /**\n * @private\n * @type {HTMLElement}\n */\n _this.collapseLabel_ = document.createElement('span');\n _this.collapseLabel_.textContent = collapseLabel;\n }\n else {\n _this.collapseLabel_ = collapseLabel;\n }\n var label = options.label !== undefined ? options.label : 'i';\n if (typeof label === 'string') {\n /**\n * @private\n * @type {HTMLElement}\n */\n _this.label_ = document.createElement('span');\n _this.label_.textContent = label;\n }\n else {\n _this.label_ = label;\n }\n var activeLabel = (_this.collapsible_ && !_this.collapsed_) ?\n _this.collapseLabel_ : _this.label_;\n var button = document.createElement('button');\n button.setAttribute('type', 'button');\n button.title = tipLabel;\n button.appendChild(activeLabel);\n button.addEventListener(EventType.CLICK, _this.handleClick_.bind(_this), false);\n var cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL +\n (_this.collapsed_ && _this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') +\n (_this.collapsible_ ? '' : ' ol-uncollapsible');\n var element = _this.element;\n element.className = cssClasses;\n element.appendChild(_this.ulElement_);\n element.appendChild(button);\n /**\n * A list of currently rendered resolutions.\n * @type {Array}\n * @private\n */\n _this.renderedAttributions_ = [];\n /**\n * @private\n * @type {boolean}\n */\n _this.renderedVisible_ = true;\n return _this;\n }\n /**\n * Collect a list of visible attributions and set the collapsible state.\n * @param {import(\"../PluggableMap.js\").FrameState} frameState Frame state.\n * @return {Array} Attributions.\n * @private\n */\n Attribution.prototype.collectSourceAttributions_ = function (frameState) {\n /**\n * Used to determine if an attribution already exists.\n * @type {!Object}\n */\n var lookup = {};\n /**\n * A list of visible attributions.\n * @type {Array}\n */\n var visibleAttributions = [];\n var layerStatesArray = frameState.layerStatesArray;\n for (var i = 0, ii = layerStatesArray.length; i < ii; ++i) {\n var layerState = layerStatesArray[i];\n if (!inView(layerState, frameState.viewState)) {\n continue;\n }\n var source = /** @type {import(\"../layer/Layer.js\").default} */ (layerState.layer).getSource();\n if (!source) {\n continue;\n }\n var attributionGetter = source.getAttributions();\n if (!attributionGetter) {\n continue;\n }\n var attributions = attributionGetter(frameState);\n if (!attributions) {\n continue;\n }\n if (!this.overrideCollapsible_ && source.getAttributionsCollapsible() === false) {\n this.setCollapsible(false);\n }\n if (Array.isArray(attributions)) {\n for (var j = 0, jj = attributions.length; j < jj; ++j) {\n if (!(attributions[j] in lookup)) {\n visibleAttributions.push(attributions[j]);\n lookup[attributions[j]] = true;\n }\n }\n }\n else {\n if (!(attributions in lookup)) {\n visibleAttributions.push(attributions);\n lookup[attributions] = true;\n }\n }\n }\n return visibleAttributions;\n };\n /**\n * @private\n * @param {?import(\"../PluggableMap.js\").FrameState} frameState Frame state.\n */\n Attribution.prototype.updateElement_ = function (frameState) {\n if (!frameState) {\n if (this.renderedVisible_) {\n this.element.style.display = 'none';\n this.renderedVisible_ = false;\n }\n return;\n }\n var attributions = this.collectSourceAttributions_(frameState);\n var visible = attributions.length > 0;\n if (this.renderedVisible_ != visible) {\n this.element.style.display = visible ? '' : 'none';\n this.renderedVisible_ = visible;\n }\n if (equals(attributions, this.renderedAttributions_)) {\n return;\n }\n removeChildren(this.ulElement_);\n // append the attributions\n for (var i = 0, ii = attributions.length; i < ii; ++i) {\n var element = document.createElement('li');\n element.innerHTML = attributions[i];\n this.ulElement_.appendChild(element);\n }\n this.renderedAttributions_ = attributions;\n };\n /**\n * @param {MouseEvent} event The event to handle\n * @private\n */\n Attribution.prototype.handleClick_ = function (event) {\n event.preventDefault();\n this.handleToggle_();\n };\n /**\n * @private\n */\n Attribution.prototype.handleToggle_ = function () {\n this.element.classList.toggle(CLASS_COLLAPSED);\n if (this.collapsed_) {\n replaceNode(this.collapseLabel_, this.label_);\n }\n else {\n replaceNode(this.label_, this.collapseLabel_);\n }\n this.collapsed_ = !this.collapsed_;\n };\n /**\n * Return `true` if the attribution is collapsible, `false` otherwise.\n * @return {boolean} True if the widget is collapsible.\n * @api\n */\n Attribution.prototype.getCollapsible = function () {\n return this.collapsible_;\n };\n /**\n * Set whether the attribution should be collapsible.\n * @param {boolean} collapsible True if the widget is collapsible.\n * @api\n */\n Attribution.prototype.setCollapsible = function (collapsible) {\n if (this.collapsible_ === collapsible) {\n return;\n }\n this.collapsible_ = collapsible;\n this.element.classList.toggle('ol-uncollapsible');\n if (!collapsible && this.collapsed_) {\n this.handleToggle_();\n }\n };\n /**\n * Collapse or expand the attribution according to the passed parameter. Will\n * not do anything if the attribution isn't collapsible or if the current\n * collapsed state is already the one requested.\n * @param {boolean} collapsed True if the widget is collapsed.\n * @api\n */\n Attribution.prototype.setCollapsed = function (collapsed) {\n if (!this.collapsible_ || this.collapsed_ === collapsed) {\n return;\n }\n this.handleToggle_();\n };\n /**\n * Return `true` when the attribution is currently collapsed or `false`\n * otherwise.\n * @return {boolean} True if the widget is collapsed.\n * @api\n */\n Attribution.prototype.getCollapsed = function () {\n return this.collapsed_;\n };\n return Attribution;\n}(Control));\n/**\n * Update the attribution element.\n * @param {import(\"../MapEvent.js\").default} mapEvent Map event.\n * @this {Attribution}\n * @api\n */\nexport function render(mapEvent) {\n this.updateElement_(mapEvent.frameState);\n}\nexport default Attribution;\n//# sourceMappingURL=Attribution.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/control/Rotate\n */\nimport Control from './Control.js';\nimport { CLASS_CONTROL, CLASS_HIDDEN, CLASS_UNSELECTABLE } from '../css.js';\nimport { easeOut } from '../easing.js';\nimport EventType from '../events/EventType.js';\n/**\n * @typedef {Object} Options\n * @property {string} [className='ol-rotate'] CSS class name.\n * @property {string|HTMLElement} [label='⇧'] Text label to use for the rotate button.\n * Instead of text, also an element (e.g. a `span` element) can be used.\n * @property {string} [tipLabel='Reset rotation'] Text label to use for the rotate tip.\n * @property {number} [duration=250] Animation duration in milliseconds.\n * @property {boolean} [autoHide=true] Hide the control when rotation is 0.\n * @property {function(import(\"../MapEvent.js\").default)} [render] Function called when the control should\n * be re-rendered. This is called in a `requestAnimationFrame` callback.\n * @property {function()} [resetNorth] Function called when the control is clicked.\n * This will override the default `resetNorth`.\n * @property {HTMLElement|string} [target] Specify a target if you want the control to be\n * rendered outside of the map's viewport.\n */\n/**\n * @classdesc\n * A button control to reset rotation to 0.\n * To style this control use css selector `.ol-rotate`. A `.ol-hidden` css\n * selector is added to the button when the rotation is 0.\n *\n * @api\n */\nvar Rotate = /** @class */ (function (_super) {\n __extends(Rotate, _super);\n /**\n * @param {Options=} opt_options Rotate options.\n */\n function Rotate(opt_options) {\n var _this = this;\n var options = opt_options ? opt_options : {};\n _this = _super.call(this, {\n element: document.createElement('div'),\n render: options.render || render,\n target: options.target\n }) || this;\n var className = options.className !== undefined ? options.className : 'ol-rotate';\n var label = options.label !== undefined ? options.label : '\\u21E7';\n /**\n * @type {HTMLElement}\n * @private\n */\n _this.label_ = null;\n if (typeof label === 'string') {\n _this.label_ = document.createElement('span');\n _this.label_.className = 'ol-compass';\n _this.label_.textContent = label;\n }\n else {\n _this.label_ = label;\n _this.label_.classList.add('ol-compass');\n }\n var tipLabel = options.tipLabel ? options.tipLabel : 'Reset rotation';\n var button = document.createElement('button');\n button.className = className + '-reset';\n button.setAttribute('type', 'button');\n button.title = tipLabel;\n button.appendChild(_this.label_);\n button.addEventListener(EventType.CLICK, _this.handleClick_.bind(_this), false);\n var cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;\n var element = _this.element;\n element.className = cssClasses;\n element.appendChild(button);\n _this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined;\n /**\n * @type {number}\n * @private\n */\n _this.duration_ = options.duration !== undefined ? options.duration : 250;\n /**\n * @type {boolean}\n * @private\n */\n _this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true;\n /**\n * @private\n * @type {number|undefined}\n */\n _this.rotation_ = undefined;\n if (_this.autoHide_) {\n _this.element.classList.add(CLASS_HIDDEN);\n }\n return _this;\n }\n /**\n * @param {MouseEvent} event The event to handle\n * @private\n */\n Rotate.prototype.handleClick_ = function (event) {\n event.preventDefault();\n if (this.callResetNorth_ !== undefined) {\n this.callResetNorth_();\n }\n else {\n this.resetNorth_();\n }\n };\n /**\n * @private\n */\n Rotate.prototype.resetNorth_ = function () {\n var map = this.getMap();\n var view = map.getView();\n if (!view) {\n // the map does not have a view, so we can't act\n // upon it\n return;\n }\n if (view.getRotation() !== undefined) {\n if (this.duration_ > 0) {\n view.animate({\n rotation: 0,\n duration: this.duration_,\n easing: easeOut\n });\n }\n else {\n view.setRotation(0);\n }\n }\n };\n return Rotate;\n}(Control));\n/**\n * Update the rotate control element.\n * @param {import(\"../MapEvent.js\").default} mapEvent Map event.\n * @this {Rotate}\n * @api\n */\nexport function render(mapEvent) {\n var frameState = mapEvent.frameState;\n if (!frameState) {\n return;\n }\n var rotation = frameState.viewState.rotation;\n if (rotation != this.rotation_) {\n var transform = 'rotate(' + rotation + 'rad)';\n if (this.autoHide_) {\n var contains = this.element.classList.contains(CLASS_HIDDEN);\n if (!contains && rotation === 0) {\n this.element.classList.add(CLASS_HIDDEN);\n }\n else if (contains && rotation !== 0) {\n this.element.classList.remove(CLASS_HIDDEN);\n }\n }\n this.label_.style.transform = transform;\n }\n this.rotation_ = rotation;\n}\nexport default Rotate;\n//# sourceMappingURL=Rotate.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/control/Zoom\n */\nimport EventType from '../events/EventType.js';\nimport Control from './Control.js';\nimport { CLASS_CONTROL, CLASS_UNSELECTABLE } from '../css.js';\nimport { easeOut } from '../easing.js';\n/**\n * @typedef {Object} Options\n * @property {number} [duration=250] Animation duration in milliseconds.\n * @property {string} [className='ol-zoom'] CSS class name.\n * @property {string|HTMLElement} [zoomInLabel='+'] Text label to use for the zoom-in\n * button. Instead of text, also an element (e.g. a `span` element) can be used.\n * @property {string|HTMLElement} [zoomOutLabel='-'] Text label to use for the zoom-out button.\n * Instead of text, also an element (e.g. a `span` element) can be used.\n * @property {string} [zoomInTipLabel='Zoom in'] Text label to use for the button tip.\n * @property {string} [zoomOutTipLabel='Zoom out'] Text label to use for the button tip.\n * @property {number} [delta=1] The zoom delta applied on each click.\n * @property {HTMLElement|string} [target] Specify a target if you want the control to be\n * rendered outside of the map's viewport.\n */\n/**\n * @classdesc\n * A control with 2 buttons, one for zoom in and one for zoom out.\n * This control is one of the default controls of a map. To style this control\n * use css selectors `.ol-zoom-in` and `.ol-zoom-out`.\n *\n * @api\n */\nvar Zoom = /** @class */ (function (_super) {\n __extends(Zoom, _super);\n /**\n * @param {Options=} opt_options Zoom options.\n */\n function Zoom(opt_options) {\n var _this = this;\n var options = opt_options ? opt_options : {};\n _this = _super.call(this, {\n element: document.createElement('div'),\n target: options.target\n }) || this;\n var className = options.className !== undefined ? options.className : 'ol-zoom';\n var delta = options.delta !== undefined ? options.delta : 1;\n var zoomInLabel = options.zoomInLabel !== undefined ? options.zoomInLabel : '+';\n var zoomOutLabel = options.zoomOutLabel !== undefined ? options.zoomOutLabel : '\\u2212';\n var zoomInTipLabel = options.zoomInTipLabel !== undefined ?\n options.zoomInTipLabel : 'Zoom in';\n var zoomOutTipLabel = options.zoomOutTipLabel !== undefined ?\n options.zoomOutTipLabel : 'Zoom out';\n var inElement = document.createElement('button');\n inElement.className = className + '-in';\n inElement.setAttribute('type', 'button');\n inElement.title = zoomInTipLabel;\n inElement.appendChild(typeof zoomInLabel === 'string' ? document.createTextNode(zoomInLabel) : zoomInLabel);\n inElement.addEventListener(EventType.CLICK, _this.handleClick_.bind(_this, delta), false);\n var outElement = document.createElement('button');\n outElement.className = className + '-out';\n outElement.setAttribute('type', 'button');\n outElement.title = zoomOutTipLabel;\n outElement.appendChild(typeof zoomOutLabel === 'string' ? document.createTextNode(zoomOutLabel) : zoomOutLabel);\n outElement.addEventListener(EventType.CLICK, _this.handleClick_.bind(_this, -delta), false);\n var cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;\n var element = _this.element;\n element.className = cssClasses;\n element.appendChild(inElement);\n element.appendChild(outElement);\n /**\n * @type {number}\n * @private\n */\n _this.duration_ = options.duration !== undefined ? options.duration : 250;\n return _this;\n }\n /**\n * @param {number} delta Zoom delta.\n * @param {MouseEvent} event The event to handle\n * @private\n */\n Zoom.prototype.handleClick_ = function (delta, event) {\n event.preventDefault();\n this.zoomByDelta_(delta);\n };\n /**\n * @param {number} delta Zoom delta.\n * @private\n */\n Zoom.prototype.zoomByDelta_ = function (delta) {\n var map = this.getMap();\n var view = map.getView();\n if (!view) {\n // the map does not have a view, so we can't act\n // upon it\n return;\n }\n var currentZoom = view.getZoom();\n if (currentZoom !== undefined) {\n var newZoom = view.getConstrainedZoom(currentZoom + delta);\n if (this.duration_ > 0) {\n if (view.getAnimating()) {\n view.cancelAnimations();\n }\n view.animate({\n zoom: newZoom,\n duration: this.duration_,\n easing: easeOut\n });\n }\n else {\n view.setZoom(newZoom);\n }\n }\n };\n return Zoom;\n}(Control));\nexport default Zoom;\n//# sourceMappingURL=Zoom.js.map","/**\n * @module ol/control\n */\nimport Collection from './Collection.js';\nimport Attribution from './control/Attribution.js';\nimport Rotate from './control/Rotate.js';\nimport Zoom from './control/Zoom.js';\nexport { default as Attribution } from './control/Attribution.js';\nexport { default as Control } from './control/Control.js';\nexport { default as FullScreen } from './control/FullScreen.js';\nexport { default as MousePosition } from './control/MousePosition.js';\nexport { default as OverviewMap } from './control/OverviewMap.js';\nexport { default as Rotate } from './control/Rotate.js';\nexport { default as ScaleLine } from './control/ScaleLine.js';\nexport { default as Zoom } from './control/Zoom.js';\nexport { default as ZoomSlider } from './control/ZoomSlider.js';\nexport { default as ZoomToExtent } from './control/ZoomToExtent.js';\n/**\n * @typedef {Object} DefaultsOptions\n * @property {boolean} [attribution=true] Include\n * {@link module:ol/control/Attribution~Attribution}.\n * @property {import(\"./control/Attribution.js\").Options} [attributionOptions]\n * Options for {@link module:ol/control/Attribution~Attribution}.\n * @property {boolean} [rotate=true] Include\n * {@link module:ol/control/Rotate~Rotate}.\n * @property {import(\"./control/Rotate.js\").Options} [rotateOptions] Options\n * for {@link module:ol/control/Rotate~Rotate}.\n * @property {boolean} [zoom] Include {@link module:ol/control/Zoom~Zoom}.\n * @property {import(\"./control/Zoom.js\").Options} [zoomOptions] Options for\n * {@link module:ol/control/Zoom~Zoom}.\n * @api\n */\n/**\n * Set of controls included in maps by default. Unless configured otherwise,\n * this returns a collection containing an instance of each of the following\n * controls:\n * * {@link module:ol/control/Zoom~Zoom}\n * * {@link module:ol/control/Rotate~Rotate}\n * * {@link module:ol/control/Attribution~Attribution}\n *\n * @param {DefaultsOptions=} opt_options\n * Defaults options.\n * @return {Collection}\n * Controls.\n * @api\n */\nexport function defaults(opt_options) {\n var options = opt_options ? opt_options : {};\n var controls = new Collection();\n var zoomControl = options.zoom !== undefined ? options.zoom : true;\n if (zoomControl) {\n controls.push(new Zoom(options.zoomOptions));\n }\n var rotateControl = options.rotate !== undefined ? options.rotate : true;\n if (rotateControl) {\n controls.push(new Rotate(options.rotateOptions));\n }\n var attributionControl = options.attribution !== undefined ?\n options.attribution : true;\n if (attributionControl) {\n controls.push(new Attribution(options.attributionOptions));\n }\n return controls;\n}\n//# sourceMappingURL=control.js.map","/**\n * @module ol/interaction/Property\n */\n/**\n * @enum {string}\n */\nexport default {\n ACTIVE: 'active'\n};\n//# sourceMappingURL=Property.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/interaction/Interaction\n */\nimport BaseObject from '../Object.js';\nimport { easeOut, linear } from '../easing.js';\nimport InteractionProperty from './Property.js';\n/**\n * Object literal with config options for interactions.\n * @typedef {Object} InteractionOptions\n * @property {function(import(\"../MapBrowserEvent.js\").default):boolean} handleEvent\n * Method called by the map to notify the interaction that a browser event was\n * dispatched to the map. If the function returns a falsy value, propagation of\n * the event to other interactions in the map's interactions chain will be\n * prevented (this includes functions with no explicit return). The interactions\n * are traversed in reverse order of the interactions collection of the map.\n */\n/**\n * @classdesc\n * Abstract base class; normally only used for creating subclasses and not\n * instantiated in apps.\n * User actions that change the state of the map. Some are similar to controls,\n * but are not associated with a DOM element.\n * For example, {@link module:ol/interaction/KeyboardZoom~KeyboardZoom} is\n * functionally the same as {@link module:ol/control/Zoom~Zoom}, but triggered\n * by a keyboard event not a button element event.\n * Although interactions do not have a DOM element, some of them do render\n * vectors and so are visible on the screen.\n * @api\n */\nvar Interaction = /** @class */ (function (_super) {\n __extends(Interaction, _super);\n /**\n * @param {InteractionOptions} options Options.\n */\n function Interaction(options) {\n var _this = _super.call(this) || this;\n if (options.handleEvent) {\n _this.handleEvent = options.handleEvent;\n }\n /**\n * @private\n * @type {import(\"../PluggableMap.js\").default}\n */\n _this.map_ = null;\n _this.setActive(true);\n return _this;\n }\n /**\n * Return whether the interaction is currently active.\n * @return {boolean} `true` if the interaction is active, `false` otherwise.\n * @observable\n * @api\n */\n Interaction.prototype.getActive = function () {\n return /** @type {boolean} */ (this.get(InteractionProperty.ACTIVE));\n };\n /**\n * Get the map associated with this interaction.\n * @return {import(\"../PluggableMap.js\").default} Map.\n * @api\n */\n Interaction.prototype.getMap = function () {\n return this.map_;\n };\n /**\n * Handles the {@link module:ol/MapBrowserEvent map browser event}.\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} `false` to stop event propagation.\n * @api\n */\n Interaction.prototype.handleEvent = function (mapBrowserEvent) {\n return true;\n };\n /**\n * Activate or deactivate the interaction.\n * @param {boolean} active Active.\n * @observable\n * @api\n */\n Interaction.prototype.setActive = function (active) {\n this.set(InteractionProperty.ACTIVE, active);\n };\n /**\n * Remove the interaction from its current map and attach it to the new map.\n * Subclasses may set up event handlers to get notified about changes to\n * the map here.\n * @param {import(\"../PluggableMap.js\").default} map Map.\n */\n Interaction.prototype.setMap = function (map) {\n this.map_ = map;\n };\n return Interaction;\n}(BaseObject));\n/**\n * @param {import(\"../View.js\").default} view View.\n * @param {import(\"../coordinate.js\").Coordinate} delta Delta.\n * @param {number=} opt_duration Duration.\n */\nexport function pan(view, delta, opt_duration) {\n var currentCenter = view.getCenterInternal();\n if (currentCenter) {\n var center = [currentCenter[0] + delta[0], currentCenter[1] + delta[1]];\n view.animateInternal({\n duration: opt_duration !== undefined ? opt_duration : 250,\n easing: linear,\n center: view.getConstrainedCenter(center)\n });\n }\n}\n/**\n * @param {import(\"../View.js\").default} view View.\n * @param {number} delta Delta from previous zoom level.\n * @param {import(\"../coordinate.js\").Coordinate=} opt_anchor Anchor coordinate in the user projection.\n * @param {number=} opt_duration Duration.\n */\nexport function zoomByDelta(view, delta, opt_anchor, opt_duration) {\n var currentZoom = view.getZoom();\n if (currentZoom === undefined) {\n return;\n }\n var newZoom = view.getConstrainedZoom(currentZoom + delta);\n var newResolution = view.getResolutionForZoom(newZoom);\n if (view.getAnimating()) {\n view.cancelAnimations();\n }\n view.animate({\n resolution: newResolution,\n anchor: opt_anchor,\n duration: opt_duration !== undefined ? opt_duration : 250,\n easing: easeOut\n });\n}\nexport default Interaction;\n//# sourceMappingURL=Interaction.js.map","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\n/**\n * @module ol/interaction/DoubleClickZoom\n */\nimport MapBrowserEventType from '../MapBrowserEventType.js';\nimport Interaction, { zoomByDelta } from './Interaction.js';\n/**\n * @typedef {Object} Options\n * @property {number} [duration=250] Animation duration in milliseconds.\n * @property {number} [delta=1] The zoom delta applied on each double click.\n */\n/**\n * @classdesc\n * Allows the user to zoom by double-clicking on the map.\n * @api\n */\nvar DoubleClickZoom = /** @class */ (function (_super) {\n __extends(DoubleClickZoom, _super);\n /**\n * @param {Options=} opt_options Options.\n */\n function DoubleClickZoom(opt_options) {\n var _this = _super.call(this, {\n handleEvent: handleEvent\n }) || this;\n var options = opt_options ? opt_options : {};\n /**\n * @private\n * @type {number}\n */\n _this.delta_ = options.delta ? options.delta : 1;\n /**\n * @private\n * @type {number}\n */\n _this.duration_ = options.duration !== undefined ? options.duration : 250;\n return _this;\n }\n return DoubleClickZoom;\n}(Interaction));\n/**\n * Handles the {@link module:ol/MapBrowserEvent map browser event} (if it was a\n * doubleclick) and eventually zooms the map.\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} `false` to stop event propagation.\n * @this {DoubleClickZoom}\n */\nfunction handleEvent(mapBrowserEvent) {\n var stopEvent = false;\n if (mapBrowserEvent.type == MapBrowserEventType.DBLCLICK) {\n var browserEvent = /** @type {MouseEvent} */ (mapBrowserEvent.originalEvent);\n var map = mapBrowserEvent.map;\n var anchor = mapBrowserEvent.coordinate;\n var delta = browserEvent.shiftKey ? -this.delta_ : this.delta_;\n var view = map.getView();\n zoomByDelta(view, delta, anchor, this.duration_);\n mapBrowserEvent.preventDefault();\n stopEvent = true;\n }\n return !stopEvent;\n}\nexport default DoubleClickZoom;\n//# sourceMappingURL=DoubleClickZoom.js.map","/**\n * @module ol/events/condition\n */\nimport MapBrowserEventType from '../MapBrowserEventType.js';\nimport { assert } from '../asserts.js';\nimport { TRUE, FALSE } from '../functions.js';\nimport { WEBKIT, MAC } from '../has.js';\n/**\n * A function that takes an {@link module:ol/MapBrowserEvent} and returns a\n * `{boolean}`. If the condition is met, true should be returned.\n *\n * @typedef {function(this: ?, import(\"../MapBrowserEvent.js\").default): boolean} Condition\n */\n/**\n * Return `true` if only the alt-key is pressed, `false` otherwise (e.g. when\n * additionally the shift-key is pressed).\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} True if only the alt key is pressed.\n * @api\n */\nexport var altKeyOnly = function (mapBrowserEvent) {\n var originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);\n return (originalEvent.altKey &&\n !(originalEvent.metaKey || originalEvent.ctrlKey) &&\n !originalEvent.shiftKey);\n};\n/**\n * Return `true` if only the alt-key and shift-key is pressed, `false` otherwise\n * (e.g. when additionally the platform-modifier-key is pressed).\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} True if only the alt and shift keys are pressed.\n * @api\n */\nexport var altShiftKeysOnly = function (mapBrowserEvent) {\n var originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);\n return (originalEvent.altKey &&\n !(originalEvent.metaKey || originalEvent.ctrlKey) &&\n originalEvent.shiftKey);\n};\n/**\n * Return `true` if the map has the focus. This condition requires a map target\n * element with a `tabindex` attribute, e.g. `
`.\n *\n * @param {import(\"../MapBrowserEvent.js\").default} event Map browser event.\n * @return {boolean} The map has the focus.\n * @api\n */\nexport var focus = function (event) {\n return event.target.getTargetElement() === document.activeElement;\n};\n/**\n * Return always true.\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} True.\n * @api\n */\nexport var always = TRUE;\n/**\n * Return `true` if the event is a `click` event, `false` otherwise.\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} True if the event is a map `click` event.\n * @api\n */\nexport var click = function (mapBrowserEvent) {\n return mapBrowserEvent.type == MapBrowserEventType.CLICK;\n};\n/**\n * Return `true` if the event has an \"action\"-producing mouse button.\n *\n * By definition, this includes left-click on windows/linux, and left-click\n * without the ctrl key on Macs.\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} The result.\n */\nexport var mouseActionButton = function (mapBrowserEvent) {\n var originalEvent = /** @type {MouseEvent} */ (mapBrowserEvent.originalEvent);\n return originalEvent.button == 0 &&\n !(WEBKIT && MAC && originalEvent.ctrlKey);\n};\n/**\n * Return always false.\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} False.\n * @api\n */\nexport var never = FALSE;\n/**\n * Return `true` if the browser event is a `pointermove` event, `false`\n * otherwise.\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} True if the browser event is a `pointermove` event.\n * @api\n */\nexport var pointerMove = function (mapBrowserEvent) {\n return mapBrowserEvent.type == 'pointermove';\n};\n/**\n * Return `true` if the event is a map `singleclick` event, `false` otherwise.\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} True if the event is a map `singleclick` event.\n * @api\n */\nexport var singleClick = function (mapBrowserEvent) {\n return mapBrowserEvent.type == MapBrowserEventType.SINGLECLICK;\n};\n/**\n * Return `true` if the event is a map `dblclick` event, `false` otherwise.\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} True if the event is a map `dblclick` event.\n * @api\n */\nexport var doubleClick = function (mapBrowserEvent) {\n return mapBrowserEvent.type == MapBrowserEventType.DBLCLICK;\n};\n/**\n * Return `true` if no modifier key (alt-, shift- or platform-modifier-key) is\n * pressed.\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} True only if there no modifier keys are pressed.\n * @api\n */\nexport var noModifierKeys = function (mapBrowserEvent) {\n var originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);\n return (!originalEvent.altKey &&\n !(originalEvent.metaKey || originalEvent.ctrlKey) &&\n !originalEvent.shiftKey);\n};\n/**\n * Return `true` if only the platform-modifier-key (the meta-key on Mac,\n * ctrl-key otherwise) is pressed, `false` otherwise (e.g. when additionally\n * the shift-key is pressed).\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} True if only the platform modifier key is pressed.\n * @api\n */\nexport var platformModifierKeyOnly = function (mapBrowserEvent) {\n var originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);\n return !originalEvent.altKey &&\n (MAC ? originalEvent.metaKey : originalEvent.ctrlKey) &&\n !originalEvent.shiftKey;\n};\n/**\n * Return `true` if only the shift-key is pressed, `false` otherwise (e.g. when\n * additionally the alt-key is pressed).\n *\n * @param {import(\"../MapBrowserEvent.js\").default} mapBrowserEvent Map browser event.\n * @return {boolean} True if only the shift key is pressed.\n * @api\n */\nexport var shiftKeyOnly = function (mapBrowserEvent) {\n var originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);\n return (!originalEvent.altKey &&\n !(originalEvent.metaKey || originalEvent.ctrlKey) &&\n originalEvent.shiftKey);\n};\n/**\n * Return `true` if the target element is not editable, i.e. not a ``-,\n * `
" + - "comma separated list of attachment identifier
" + - "" + - ""; - - } - - - /** - * List available attachments in database - * @return JSON object containing all attachments available in database - * @throws JSONException if there is error when encoding to JSON - */ - @RequestMapping(value="/attachments", - method = RequestMethod.GET, - produces = "application/json; charset=utf-8") - @ResponseBody - public String attachments() throws JSONException { - JSONArray attachments = new JSONArray(); - for(Attachment att : this.attachmentRepo.findAll()){ - JSONObject attachment = new JSONObject(); - attachment.put("id",att.getId()); - attachment.put("name",att.getName()); - attachment.put("mimeType",att.getMimeType()); - attachments.put(attachment); - } - - JSONObject res = new JSONObject(); - res.put("attachments", attachments); - return res.toString(); - } - - /** - * Generate a list of template found in database - * @return JSON object containing all templates found in database - * @throws JSONException if templates cannot be encoded to JSON - */ - @RequestMapping(value="/emailTemplates", - method = RequestMethod.GET, - produces = "application/json; charset=utf-8") - @ResponseBody - public String emailTemplates() throws JSONException { - JSONArray emailTemplates = new JSONArray(); - for(EmailTemplate temp : this.emailTemplateRepo.findAll()){ - JSONObject attachment = new JSONObject(); - attachment.put("id", temp.getId()); - attachment.put("name", temp.getName()); - attachment.put("content", temp.getContent()); - emailTemplates.put(attachment); - } - - JSONObject res = new JSONObject(); - res.put("templates", emailTemplates); - return res.toString(); - } - - - @RequestMapping(value="/testShadowExpire", method = RequestMethod.GET) - @ResponseBody - public String testAttachments() throws JSONException { - - List accounts = this.accountDao.findByShadowExpire(); - JSONObject res = new JSONObject(); - JSONArray tempAccounts = new JSONArray(); - for(Account acc : accounts) - tempAccounts.put(acc.toJSON()); - res.put("temporaryAccounts",tempAccounts); - - return res.toString(); - } - - - /** - * Send EmailEntry to smtp server - * - * @param email email to send - * @throws NotFoundException if recipient cannot be found in LDAP server - * @throws DataServiceException if LDAP server is not available - * @throws MessagingException if some field of email cannot be encoded (malformed email address) - */ - private void send(EmailEntry email) throws NotFoundException, DataServiceException, MessagingException { - - final Properties props = System.getProperties(); - props.put("mail.smtp.host", this.emailFactory.getSmtpHost()); - props.put("mail.protocol.port", this.emailFactory.getSmtpPort()); - - final Session session = Session.getInstance(props, null); - final MimeMessage message = new MimeMessage(session); - - Account recipient = this.accountDao.findByUUID(email.getRecipient()); - InternetAddress[] senders = {new InternetAddress(this.accountDao.findByUUID(email.getSender()).getEmail())}; - - message.addFrom(senders); - message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient.getEmail())); - message.setSubject(email.getSubject()); - message.setHeader("Date", (new MailDateFormat()).format(email.getDate())); - - // Mail content - Multipart multiPart = new MimeMultipart("alternative"); - - // attachments - for(Attachment att : email.getAttachments()) { - MimeBodyPart mbp = new MimeBodyPart(); - mbp.setDataHandler(new DataHandler(new ByteArrayDataSource(att.getContent(), att.getMimeType()))); - mbp.setFileName(att.getName()); - multiPart.addBodyPart(mbp); - } - - // html part - MimeBodyPart htmlPart = new MimeBodyPart(); - htmlPart.setContent(email.getBody(), "text/html; charset=utf-8"); - multiPart.addBodyPart(htmlPart); - - message.setContent(multiPart); - - // Send message - Transport.send(message); - } - -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/emails/package-info.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/emails/package-info.java deleted file mode 100644 index e2e6d56ebf..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/emails/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * This package holds information about emails sent from ldapdamin UI - * - * Email can contain attachments stored in DB - */ -package org.georchestra.ldapadmin.ws.emails; \ No newline at end of file diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/newaccount/AccountFormBean.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/newaccount/AccountFormBean.java deleted file mode 100644 index f8a6515d43..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/newaccount/AccountFormBean.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * - */ -package org.georchestra.ldapadmin.ws.newaccount; - -import java.io.Serializable; - - -/** - * This model maintains the account form data. - * - * @author Mauricio Pazos - * - */ -public class AccountFormBean implements Serializable{ - - private static final long serialVersionUID = 6955470190631684934L; - - private String uid; - private String firstName; - private String surname; - - private String org; - private String title; - private String email; - private String phone; - private String description; - private String password; - private String confirmPassword; - - private String recaptcha_challenge_field; - private String recaptcha_response_field; - - public String getRecaptcha_challenge_field() { - return recaptcha_challenge_field; - } - public void setRecaptcha_challenge_field(String recaptcha_challenge_field) { - this.recaptcha_challenge_field = recaptcha_challenge_field; - } - public String getRecaptcha_response_field() { - return recaptcha_response_field; - } - public void setRecaptcha_response_field(String recaptcha_response_field) { - this.recaptcha_response_field = recaptcha_response_field; - } - - - public String getUid() { - return uid; - } - public void setUid(String uid) { - this.uid = uid; - } - public String getFirstName() { - return firstName; - } - public void setFirstName(String name) { - this.firstName = name; - } - public String getOrg() { - return org; - } - public void setOrg(String org) { - this.org = org; - } - public String getTitle() { - return title; - } - public void setTitle(String title) { - this.title = title; - } - - public String getEmail() { - return email; - } - public void setEmail(String email) { - this.email = email; - } - public String getPhone() { - return phone; - } - public void setPhone(String phone) { - this.phone = phone; - } - public String getDescription() { - return description; - } - public void setDescription(String description) { - this.description = description; - } - - public String getPassword() { - return password; - } - public void setPassword(String password) { - this.password = password; - } - public String getConfirmPassword() { - return confirmPassword; - } - public void setConfirmPassword(String confirmPassword) { - this.confirmPassword = confirmPassword; - } - - public String getSurname() { - return surname; - } - - public void setSurname(String sn){ - - this.surname = sn; - } - @Override - public String toString() { - return "AccountFormBean [uid=" + uid + ", firstName=" + firstName - + ", surname=" + surname + ", org=" + org + ", title=" + title - + ", email=" + email - + ", phone=" + phone + ", description=" + description + ", password=" - + password + ", confirmPassword=" + confirmPassword - + ", recaptcha_challenge_field=" + recaptcha_challenge_field - + ", recaptcha_response_field=" + recaptcha_response_field - + "]"; - } - - - -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/newaccount/NewAccountFormController.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/newaccount/NewAccountFormController.java deleted file mode 100644 index 9372dd4edb..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/newaccount/NewAccountFormController.java +++ /dev/null @@ -1,201 +0,0 @@ -/** - * - */ -package org.georchestra.ldapadmin.ws.newaccount; - -import java.io.IOException; - -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; - -import net.tanesha.recaptcha.ReCaptcha; - -import org.apache.commons.lang3.ArrayUtils; -import org.georchestra.ldapadmin.bs.Moderator; -import org.georchestra.ldapadmin.bs.ReCaptchaParameters; -import org.georchestra.ldapadmin.ds.AccountDao; -import org.georchestra.ldapadmin.ds.DataServiceException; -import org.georchestra.ldapadmin.ds.DuplicatedEmailException; -import org.georchestra.ldapadmin.ds.DuplicatedUidException; -import org.georchestra.ldapadmin.dto.Account; -import org.georchestra.ldapadmin.dto.AccountFactory; -import org.georchestra.ldapadmin.dto.Group; -import org.georchestra.ldapadmin.mailservice.MailService; -import org.georchestra.ldapadmin.ws.utils.EmailUtils; -import org.georchestra.ldapadmin.ws.utils.PasswordUtils; -import org.georchestra.ldapadmin.ws.utils.RecaptchaUtils; -import org.georchestra.ldapadmin.ws.utils.UserUtils; -import org.georchestra.ldapadmin.ws.utils.Validation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.SessionAttributes; -import org.springframework.web.bind.support.SessionStatus; - -/** - * Manages the UI Account Form. - * - *

- * - *

- * - * @author Mauricio Pazos - * - */ -@Controller -@SessionAttributes(types={AccountFormBean.class}) -public final class NewAccountFormController { - - private AccountDao accountDao; - - private MailService mailService; - - private Moderator moderator; - - private ReCaptcha reCaptcha; - - private ReCaptchaParameters reCaptchaParameters; - - private static final String[] fields = {"firstName","surname", "email", "phone", "org", - "title", "description", "uid", "password", "confirmPassword"}; - - @Autowired - public NewAccountFormController(AccountDao dao, MailService mailSrv, Moderator moderatorRule, - ReCaptcha reCaptcha, ReCaptchaParameters reCaptchaParameters) { - this.accountDao = dao; - this.mailService = mailSrv; - this.moderator = moderatorRule; - this.reCaptcha = reCaptcha; - this.reCaptchaParameters = reCaptchaParameters; - } - - @InitBinder - public void initForm(WebDataBinder dataBinder) { - dataBinder.setAllowedFields(ArrayUtils.addAll(fields, - new String[]{"recaptcha_challenge_field", "recaptcha_response_field"})); - } - - @RequestMapping(value="/account/new", method=RequestMethod.GET) - public String setupForm(HttpServletRequest request, Model model) throws IOException{ - - HttpSession session = request.getSession(); - AccountFormBean formBean = new AccountFormBean(); - - model.addAttribute(formBean); - session.setAttribute("reCaptchaPublicKey", this.reCaptchaParameters.getPublicKey()); - for (String f : fields) { - if (Validation.isFieldRequired(f)) { - session.setAttribute(f + "Required", "true"); - } - } - return "createAccountForm"; - } - - /** - * Creates a new account in ldap. If the application was configured as "moderator singnup" the new account is added in the PENDING group, - * in other case, it will be inserted in the SV_USER group - * - * - * @param formBean - * @param result - * @param sessionStatus - * - * @return the next view - * - * @throws IOException - */ - @RequestMapping(value="/account/new", method=RequestMethod.POST) - public String create(HttpServletRequest request, - @ModelAttribute AccountFormBean formBean, - BindingResult result, - SessionStatus sessionStatus) - throws IOException { - - String remoteAddr = request.getRemoteAddr(); - - UserUtils.validate(formBean.getUid(), formBean.getFirstName(), formBean.getSurname(), result ); - EmailUtils.validate(formBean.getEmail(), result); - PasswordUtils.validate(formBean.getPassword(), formBean.getConfirmPassword(), result); - new RecaptchaUtils(remoteAddr, this.reCaptcha).validate(formBean.getRecaptcha_challenge_field(), formBean.getRecaptcha_response_field(), result); - Validation.validateField("phone", formBean.getPhone(), result); - Validation.validateField("title", formBean.getTitle(), result); - Validation.validateField("org", formBean.getOrg(), result); - Validation.validateField("description", formBean.getDescription(), result); - - if(result.hasErrors()){ - - return "createAccountForm"; - } - - // inserts the new account - try { - - Account account = AccountFactory.createBrief( - formBean.getUid().toLowerCase(), - formBean.getPassword(), - formBean.getFirstName(), - formBean.getSurname(), - formBean.getEmail(), - formBean.getPhone(), - formBean.getOrg(), - formBean.getTitle(), - formBean.getDescription() ); - - String groupID = this.moderator.moderatedSignup() ? Group.PENDING : Group.SV_USER; - - this.accountDao.insert(account, groupID); - - final ServletContext servletContext = request.getSession().getServletContext(); - if(this.moderator.moderatedSignup() ){ - - // email to the moderator - this.mailService.sendNewAccountRequiresModeration(servletContext, account.getUid(), account.getCommonName(), account.getEmail(), this.moderator.getModeratorEmail()); - - // email to the user - this.mailService.sendAccountCreationInProcess(servletContext, account.getUid(), account.getCommonName(), account.getEmail()); - } else { - // email to the user - this.mailService.sendAccountWasCreated(servletContext, account.getUid(), account.getCommonName(), account.getEmail()); - } - sessionStatus.setComplete(); - - return "welcomeNewUser"; - - } catch (DuplicatedEmailException e) { - - result.rejectValue("email", "email.error.exist", "there is a user with this e-mail"); - return "createAccountForm"; - - } catch (DuplicatedUidException e) { - - try { - String proposedUid = this.accountDao.generateUid( formBean.getUid() ); - - formBean.setUid(proposedUid); - - result.rejectValue("uid", "uid.error.exist", "the uid exist"); - - return "createAccountForm"; - - } catch (DataServiceException e1) { - throw new IOException(e); - } - - } catch (DataServiceException e) { - - throw new IOException(e); - } - } - - @ModelAttribute("accountFormBean") - public AccountFormBean getAccountFormBean() { - return new AccountFormBean(); - } -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/newaccount/UidGenerator.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/newaccount/UidGenerator.java deleted file mode 100644 index 6f492749d8..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/newaccount/UidGenerator.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * - */ -package org.georchestra.ldapadmin.ws.newaccount; - - -/** - * A number is added or increment as postfix. - * - * @author Mauricio Pazos - */ -public final class UidGenerator { - - private UidGenerator(){} - - public static String next(String uid){ - - - String str = uid.replaceAll("[^0-9]", ""); - try{ - int i = Integer.parseInt(str); - - i++; - - String replacement = String.valueOf( i ); - return uid.replace( str, replacement ); - - - } catch(Exception e){ - return uid + 1; - } - } - - -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/NewPasswordFormBean.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/NewPasswordFormBean.java deleted file mode 100644 index 3cd2c9b149..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/NewPasswordFormBean.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * - */ -package org.georchestra.ldapadmin.ws.passwordrecovery; - -import java.io.Serializable; - -/** - * Maintains the new password typed by the user. - * - * @author Mauricio Pazos - */ -public class NewPasswordFormBean implements Serializable { - - private static final long serialVersionUID = 3239632432961416372L; - - private String uid; - private String token; - private String password; - private String confirmPassword; - - - - public String getUid() { - return uid; - } - public void setUid(String uid) { - this.uid = uid; - } - public String getToken() { - return token; - } - public void setToken(String token) { - this.token = token; - } - public String getPassword() { - return password; - } - public void setPassword(String password) { - this.password = password; - } - public String getConfirmPassword() { - return confirmPassword; - } - public void setConfirmPassword(String confirmPassword) { - this.confirmPassword = confirmPassword; - } - - @Override - public String toString() { - return "NewPasswordFormBean [uid=" + uid + ", token=" + token - + ", password=" + password + ", confirmPassword=" - + confirmPassword + "]"; - } - - -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/NewPasswordFormController.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/NewPasswordFormController.java deleted file mode 100644 index 95aacfa32c..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/NewPasswordFormController.java +++ /dev/null @@ -1,151 +0,0 @@ -/** - * - */ -package org.georchestra.ldapadmin.ws.passwordrecovery; - -import java.io.IOException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.georchestra.ldapadmin.ds.AccountDao; -import org.georchestra.ldapadmin.ds.DataServiceException; -import org.georchestra.ldapadmin.ds.NotFoundException; -import org.georchestra.ldapadmin.ds.UserTokenDao; -import org.georchestra.ldapadmin.ws.utils.PasswordUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.SessionAttributes; -import org.springframework.web.bind.support.SessionStatus; - -/** - * This controller implements the interactions required to ask for a new password based on a token provide. - * - * @author Mauricio Pazos - * - */ -@Controller -@SessionAttributes(types=NewPasswordFormBean.class) -public class NewPasswordFormController { - - private static final Log LOG = LogFactory.getLog(NewPasswordFormController.class.getName()); - - private AccountDao accountDao; - private UserTokenDao userTokenDao; - - @Autowired - public NewPasswordFormController( AccountDao accountDao, UserTokenDao userTokenDao){ - this.accountDao = accountDao; - this.userTokenDao = userTokenDao; - } - - - @InitBinder - public void initForm( WebDataBinder dataBinder) { - - dataBinder.setAllowedFields(new String[]{"password", "confirmPassword"}); - } - - - /** - * Search the user associated to the provided token, then initialize the {@link NewPasswordFormBean}. - * If the token is not valid (it didn't exist in the system registry) the PasswordRecoveryForm is presented to offer a new - * chance to the user. - * - * @param token the token was generated by the {@link PasswordRecoveryFormController}} - * @param model - * - * @return newPasswordForm or passwordRecoveryForm - * - * @throws IOException - */ - @RequestMapping(value="/account/newPassword", method=RequestMethod.GET) - public String setupForm(@RequestParam("token") String token, Model model) throws IOException{ - - - try{ - final String uid = this.userTokenDao.findUserByToken(token); - - NewPasswordFormBean formBean = new NewPasswordFormBean(); - - formBean.setToken(token); - formBean.setUid(uid); - - model.addAttribute(formBean); - - return "newPasswordForm"; - - } catch(NotFoundException e){ - - return "passwordRecoveryForm"; - - } catch (DataServiceException e) { - - LOG.error("cannot insert the setup the passwordRecoveryForm. " + e.getMessage()); - - throw new IOException(e); - } - - } - - /** - * Registers the new password, if it is valid. - * - * @param formBean - * @param result - * @param sessionStatus - * - * @return the next view - * - * @throws IOException - */ - @RequestMapping(value="/account/newPassword", method=RequestMethod.POST) - public String newPassword( - @ModelAttribute NewPasswordFormBean formBean, - BindingResult result, - SessionStatus sessionStatus) - throws IOException { - - - PasswordUtils.validate( formBean.getPassword(), formBean.getConfirmPassword(), result); - - - if(result.hasErrors()){ - - return "newPasswordForm"; - } - - // changes the user's password and removes the token - try { - - String uid = formBean.getUid(); - String password = formBean.getPassword(); - - this.accountDao.changePassword(uid, password); - - this.userTokenDao.delete(uid); - - sessionStatus.setComplete(); - - return "passwordUpdated"; - - } catch (DataServiceException e) { - LOG.error("cannot set the the new password. " + e.getMessage()); - - throw new IOException(e); - - } - } - - @ModelAttribute("newPasswordFormBean") - public NewPasswordFormBean getNewPasswordFormBean() { - return new NewPasswordFormBean(); - } -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/PasswordRecoveryFormBean.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/PasswordRecoveryFormBean.java deleted file mode 100644 index b6f406a807..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/PasswordRecoveryFormBean.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.georchestra.ldapadmin.ws.passwordrecovery; - -import java.io.Serializable; - -public class PasswordRecoveryFormBean implements Serializable{ - - /** - * - */ - private static final long serialVersionUID = 7773803527246666406L; - - private String email; - private String recaptcha_challenge_field; - private String recaptcha_response_field; - - @Override - public String toString() { - return "PasswordRecoveryFormBean [email=" + email - + ", recaptcha_challenge_field=" + recaptcha_challenge_field - + ", recaptcha_response_field=" + recaptcha_response_field - + "]"; - } - - - public String getEmail() { - return this.email; - } - public void setEmail(String email) { - this.email = email; - } - public String getRecaptcha_challenge_field() { - return recaptcha_challenge_field; - } - public void setRecaptcha_challenge_field(String recaptcha_challenge_field) { - this.recaptcha_challenge_field = recaptcha_challenge_field; - } - public String getRecaptcha_response_field() { - return recaptcha_response_field; - } - public void setRecaptcha_response_field(String recaptcha_response_field) { - this.recaptcha_response_field = recaptcha_response_field; - } - -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/PasswordRecoveryFormController.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/PasswordRecoveryFormController.java deleted file mode 100644 index 2c366bfa0f..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/passwordrecovery/PasswordRecoveryFormController.java +++ /dev/null @@ -1,218 +0,0 @@ -/** - * - */ -package org.georchestra.ldapadmin.ws.passwordrecovery; - -import java.io.IOException; -import java.util.List; -import java.util.UUID; - -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.georchestra.ldapadmin.Configuration; -import org.georchestra.ldapadmin.bs.ReCaptchaParameters; -import org.georchestra.ldapadmin.ds.AccountDao; -import org.georchestra.ldapadmin.ds.DataServiceException; -import org.georchestra.ldapadmin.ds.GroupDao; -import org.georchestra.ldapadmin.ds.NotFoundException; -import org.georchestra.ldapadmin.ds.UserTokenDao; -import org.georchestra.ldapadmin.dto.Account; -import org.georchestra.ldapadmin.dto.Group; -import org.georchestra.ldapadmin.mailservice.MailService; -import org.georchestra.ldapadmin.ws.utils.EmailUtils; -import org.georchestra.ldapadmin.ws.utils.RecaptchaUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.SessionAttributes; -import org.springframework.web.bind.support.SessionStatus; - -import net.tanesha.recaptcha.ReCaptcha; - -/** - * Manage the user interactions required to implement the lost password workflow: - *

- *

    - * - *
  • Present a form in order to ask for the user's mail.
  • - * - *
  • If the given email matches one of the LDAP users, an email is sent to this user with a unique http URL to reset his password.
  • - * - *
  • As result of this interaction the view EmailSentForm.jsp is presented
  • - *
- *

- * - * @author Mauricio Pazos - */ -@Controller -@SessionAttributes(types=PasswordRecoveryFormBean.class) -public class PasswordRecoveryFormController { - - protected static final Log LOG = LogFactory.getLog(PasswordRecoveryFormController.class.getName()); - - // collaborations - private AccountDao accountDao; - private GroupDao groupDao; - private MailService mailService; - private UserTokenDao userTokenDao; - private Configuration config; - private ReCaptcha reCaptcha; - private ReCaptchaParameters reCaptchaParameters; - - - @Autowired - public PasswordRecoveryFormController( AccountDao dao,GroupDao gDao, MailService mailSrv, UserTokenDao userTokenDao, - Configuration cfg, ReCaptcha reCaptcha, ReCaptchaParameters reCaptchaParameters){ - this.accountDao = dao; - this.groupDao = gDao; - this.mailService = mailSrv; - this.userTokenDao = userTokenDao; - this.config = cfg; - this.reCaptcha = reCaptcha; - this.reCaptchaParameters = reCaptchaParameters; - - } - - @InitBinder - public void initForm( WebDataBinder dataBinder) { - - dataBinder.setAllowedFields(new String[]{"email", "recaptcha_challenge_field", "recaptcha_response_field"}); - } - - @RequestMapping(value="/account/passwordRecovery", method=RequestMethod.GET) - public String setupForm(HttpServletRequest request, @RequestParam(value="email", required=false) String email, Model model) throws IOException{ - - HttpSession session = request.getSession(); - PasswordRecoveryFormBean formBean = new PasswordRecoveryFormBean(); - formBean.setEmail(email); - - model.addAttribute(formBean); - session.setAttribute("reCaptchaPublicKey", this.reCaptchaParameters.getPublicKey()); - - return "passwordRecoveryForm"; - } - - /** - * Generates a new unique http URL based on a token, then an e-mail is sent to the user with instruction to change his password. - * - * - * @param formBean Contains the user's email - * @param resultErrors will be updated with the list of found errors. - * @param sessionStatus - * - * @return the next view - * - * @throws IOException - */ - @RequestMapping(value="/account/passwordRecovery", method=RequestMethod.POST) - public String generateToken( - HttpServletRequest request, - @ModelAttribute PasswordRecoveryFormBean formBean, - BindingResult resultErrors, - SessionStatus sessionStatus) - throws IOException { - - EmailUtils.validate(formBean.getEmail(), resultErrors); - if(resultErrors.hasErrors()){ - return "passwordRecoveryForm"; - } - - - - String remoteAddr = request.getRemoteAddr(); - new RecaptchaUtils(remoteAddr, this.reCaptcha).validate(formBean.getRecaptcha_challenge_field(), formBean.getRecaptcha_response_field(), resultErrors); - if(resultErrors.hasErrors()){ - return "passwordRecoveryForm"; - } - - try { - Account account = this.accountDao.findByEmail(formBean.getEmail()); - List group = this.groupDao.findAllForUser(account.getUid()); - // Finds the user using the email as key, if it exists a new token is generated to include in the unique http URL. - - - - for (Group g : group) { - if (g.getName().equals("PENDING")) { - throw new NotFoundException("User in PENDING group"); - } - } - - String token = UUID.randomUUID().toString(); - - - - // if there is a previous token it is removed - if( this.userTokenDao.exist(account.getUid()) ) { - this.userTokenDao.delete(account.getUid()); - } - - this.userTokenDao.insertToken(account.getUid(), token); - - String contextPath = this.config.getPublicContextPath(); - String url = makeChangePasswordURL(request.getServerName(), request.getServerPort(), contextPath, token); - - ServletContext servletContext = request.getSession().getServletContext(); - this.mailService.sendChangePasswordURL(servletContext, account.getUid(), account.getCommonName(), url, account.getEmail()); - - sessionStatus.setComplete(); - - return "emailWasSent"; - - } catch (DataServiceException e) { - - throw new IOException(e); - - } catch (NotFoundException e) { - - resultErrors.rejectValue("email", "email.error.notFound", "No user found for this email."); - - return "passwordRecoveryForm"; - - } - } - - - /** - * Create the URL to change the password based on the provided token - * @param token - * @param token2 - * - * @return a new URL to change password - */ - private String makeChangePasswordURL(final String host, final int port, final String contextPath, final String token) { - - StringBuilder strBuilder = new StringBuilder(""); - if ((port == 80) || (port == 443)) { - strBuilder.append("https://").append(host); - } else { - strBuilder.append("http://").append(host).append(":").append(port); - } - strBuilder.append(contextPath); - strBuilder.append( "/account/newPassword?token=").append(token); - - String url = strBuilder.toString(); - - if(LOG.isDebugEnabled()){ - LOG.debug("generated url:" + url); - } - - return url; - } - - @ModelAttribute("passwordRecoveryFormBean") - public PasswordRecoveryFormBean getPasswordRecoveryFormBean() { - return new PasswordRecoveryFormBean(); - } -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/EmailUtils.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/EmailUtils.java deleted file mode 100644 index 896d60a609..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/EmailUtils.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * - */ -package org.georchestra.ldapadmin.ws.utils; - -import org.apache.commons.validator.routines.EmailValidator; -import org.springframework.util.StringUtils; -import org.springframework.validation.Errors; - -/** - * Utility class to manage the email. - * - * @author Mauricio Pazos - * - */ -public class EmailUtils { - - public static void validate(String email, Errors errors) { - - if ( !StringUtils.hasLength(email) && Validation.isFieldRequired("email") ) { - errors.rejectValue("email", "email.error.required", "required"); - } else { - if (!EmailValidator.getInstance().isValid(email)) { - errors.rejectValue("email", "email.error.invalidFormat", "Invalid Format"); - } - } - } - -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/PasswordUtils.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/PasswordUtils.java deleted file mode 100644 index 9bc2cf59a2..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/PasswordUtils.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * - */ -package org.georchestra.ldapadmin.ws.utils; - -import org.springframework.util.StringUtils; -import org.springframework.validation.Errors; - -/** - * - * @author Mauricio Pazos - * - */ -public final class PasswordUtils { - - public static final int SIZE = 8; - - - private PasswordUtils(){ - // utility class - } - - public static void validate(final String password, final String confirmPassword, Errors errors) { - - final String pwd1 = password.trim(); - final String pwd2 = confirmPassword.trim(); - - if( !StringUtils.hasLength(pwd1) && Validation.isFieldRequired("password") ){ - - errors.rejectValue("password", "password.error.required", "required"); - - } - if( !StringUtils.hasLength(pwd2) && Validation.isFieldRequired("confirmPassword") ){ - - errors.rejectValue("confirmPassword", "confirmPassword.error.required", "required"); - } - if( StringUtils.hasLength(pwd1) && StringUtils.hasLength(pwd2) ){ - - if(!pwd1.equals(pwd2)){ - errors.rejectValue("confirmPassword", "confirmPassword.error.pwdNotEquals", "These passwords don't match"); - - } else { - - if(pwd1.length() < SIZE ){ - errors.rejectValue("password", "password.error.sizeError", "The password does have at least 8 characters"); - } - } - } - } - -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/RecaptchaUtils.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/RecaptchaUtils.java deleted file mode 100644 index c798dad710..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/RecaptchaUtils.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.georchestra.ldapadmin.ws.utils; - -import net.tanesha.recaptcha.ReCaptcha; -import net.tanesha.recaptcha.ReCaptchaResponse; - -import org.springframework.util.StringUtils; -import org.springframework.validation.Errors; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Utility class to manage recaptcha. - * - * @author Sylvain Lesage - * - */ -public class RecaptchaUtils { - - private static final Log LOG = LogFactory.getLog(RecaptchaUtils.class.getName()); - private String remoteAddr; - private ReCaptcha reCaptcha; - - public RecaptchaUtils(final String remoteAddr, final ReCaptcha reCaptcha) { - this.remoteAddr = remoteAddr; - this.reCaptcha = reCaptcha; - } - - public void validate(final String captchaGenerated, final String userResponse, Errors errors) { - - final String trimmedCaptcha = userResponse.trim(); - - if(!StringUtils.hasLength(trimmedCaptcha)){ - LOG.info("The user response to recaptcha is empty."); - errors.rejectValue("recaptcha_response_field", "recaptcha_response_field.error.required", "required"); - } else { - - ReCaptchaResponse captchaResponse = this.reCaptcha.checkAnswer( - this.remoteAddr, - captchaGenerated, - userResponse); - if(!captchaResponse.isValid()){ - LOG.info("The user response to recaptcha is not valid. The error message is '" + captchaResponse.getErrorMessage() + "' - see Error Code Reference at https://developers.google.com/recaptcha/docs/verify."); - errors.rejectValue("recaptcha_response_field", "recaptcha_response_field.error.captchaNoMatch", "The texts didn't match"); - } else { - LOG.debug("The user response to recaptcha is valid."); - } - } - } -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/UserUtils.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/UserUtils.java deleted file mode 100644 index a897492760..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/UserUtils.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.georchestra.ldapadmin.ws.utils; - -import org.springframework.util.StringUtils; -import org.springframework.validation.Errors; - -/** - * Contains useful method that are used in the form validation. - * - * @author Mauricio Pazos - * - */ -public class UserUtils { - - private UserUtils(){ - // utility class - } - - public static void validate(String uid, String firstName, String surname, Errors errors) { - - // uid validation - if( !StringUtils.hasLength(uid) && Validation.isFieldRequired("uid") ){ - errors.rejectValue("uid", "uid.error.required", "required"); - } else{ - - if( StringUtils.hasLength(uid) && !isUidValid(uid)){ - errors.rejectValue("uid", "uid.error.invalid", "required"); - } - } - - // name validation - validate(firstName, surname, errors); - } - - /** - * A valid user identifier (uid) can only contain characters, numbers, hyphens or dot. It must begin with a character. - * - * @param uid user identifier - * @return true if the uid is valid - */ - private static boolean isUidValid(String uid) { - - char firstChar = uid.charAt(0); - if(!Character.isLetter(firstChar)){ - return false; - } - for(int i=1; i < uid.length(); i++){ - - if( !(Character.isLetter( uid.charAt(i)) || Character.isDigit( uid.charAt(i)) || ( uid.charAt(i) == '.') || ( uid.charAt(i) == '-')) ){ - - return false; - } - } - return true; - } - - public static void validate(String firstName, String surname, Errors errors) { - - if( !StringUtils.hasLength(firstName) && Validation.isFieldRequired("firstName") ){ - errors.rejectValue("firstName", "firstName.error.required", "required"); - } - - if( !StringUtils.hasLength( surname ) && Validation.isFieldRequired("surname") ){ - errors.rejectValue("surname", "surname.error.required", "required"); - } - } - - -} diff --git a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/Validation.java b/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/Validation.java deleted file mode 100644 index 2df3b61cff..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/ldapadmin/ws/utils/Validation.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * - */ -package org.georchestra.ldapadmin.ws.utils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.springframework.util.StringUtils; -import org.springframework.validation.Errors; - -/** - * Validation class for all forms - * - * @author Sylvain Lesage - * - */ -public class Validation { - - private static List requiredFields; - public String getRequiredFields() { - return requiredFields.toString(); - } - public void setRequiredFields(String csvRequiredFields) { - List r = new ArrayList(Arrays.asList(csvRequiredFields.split("\\s*,\\s*"))); - // add mandatory fields (they may be present two times, it's not a problem) - r.add("email"); - r.add("uid"); - r.add("password"); - r.add("confirmPassword"); - this.requiredFields = r; - } - public static boolean isFieldRequired (String field) { - if (requiredFields == null) - return false; - - for (String f : requiredFields) { - if (field.equals(f)) { - return true; - } - } - return false; - } - public static void validateField (String field, String value, Errors errors) { - if( Validation.isFieldRequired(field) && !StringUtils.hasLength(value) ){ - errors.rejectValue(field, "error.required", "required"); - } - } -} diff --git a/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/AbstractDataCommand.java b/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/AbstractDataCommand.java deleted file mode 100644 index 8c90fbe716..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/AbstractDataCommand.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.georchestra.lib.sqlcommand; - -import java.sql.Connection; - -public abstract class AbstractDataCommand implements DataCommand { - - protected Connection connection; - - @Override - public void setConnection(Connection connection) { - this.connection = connection; - } - - -} diff --git a/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/AbstractQueryCommand.java b/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/AbstractQueryCommand.java deleted file mode 100644 index c03e6d975c..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/AbstractQueryCommand.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * - */ -package org.georchestra.lib.sqlcommand; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - - -/** - * Maintains the abstract behavior required to execute a SQL query. - * The subclass must implement the methods: - *
- * {@link AbstractQueryCommand#prepareStatement()}
- * {@link AbstractQueryCommand#getRow(ResultSet)}
- * 
- * - * @author Mauricio Pazos - */ -public abstract class AbstractQueryCommand extends AbstractDataCommand { - - private LinkedList> resultList; - - /** - * This template method executes the sql statement specified in the prepareStatement method. - */ - @Override - public void execute() throws DataCommandException { - - assert (this.connection != null) : "database connection is null, use setConnection"; - - // executes the sql statement and populates the list with the data present in the result set - ResultSet rs = null; - PreparedStatement pStmt=null; - try { - pStmt = prepareStatement(); - - rs = pStmt.executeQuery(); - - this.resultList = new LinkedList>(); - - while (rs.next()) { - this.resultList.add( getRow(rs)); - } - - } catch (SQLException e) { - - throw new DataCommandException(e.getMessage()); - - } finally{ - try { - if(rs != null) rs.close(); - if(pStmt != null) pStmt.close(); - - } catch (SQLException e1) { - throw new DataCommandException(e1.getMessage()); - } - } - } - - /** - * The subclass must to define the sql statement to exectue - * - * @return {@link PreparedStatement}} - * @throws SQLException - */ - protected abstract PreparedStatement prepareStatement() throws SQLException; - - - /** - * Assigns the values of fields present in the {@link ResultSet} to the Map. - * @param rs - * @return a Map - * @throws SQLException - */ - protected abstract Map getRow(ResultSet rs) throws SQLException; - - - - - - public List> getResult() { - return this.resultList; - } - -} diff --git a/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/AbstractUpdateCommand.java b/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/AbstractUpdateCommand.java deleted file mode 100644 index d5a3e0603f..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/AbstractUpdateCommand.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * - */ -package org.georchestra.lib.sqlcommand; - -import java.sql.PreparedStatement; -import java.sql.SQLException; - - -/** - * Executes Insert, Update and Delete SQL command. - * - *

- * The subclass must provide the sql command to execute. To do that the {@link AbstractUpdateCommand#prepareStatement()} method - *

- * - * - * @author Mauricio Pazos - * - */ -public abstract class AbstractUpdateCommand extends AbstractDataCommand{ - - - - /** - * Execute the sql insert to add the new row (uid, token, timestamp) - * - * @see org.georchestra.ogcservstatistics.dataservices.DataCommand#execute() - */ - @Override - public void execute() throws DataCommandException { - assert this.connection != null: "database connection is null, use setConnection"; - - // executes the sql statement and checks that the update operation will be inserted one row in the table - PreparedStatement pStmt=null; - try { - this.connection.setAutoCommit(false); - pStmt = prepareStatement(); - int updatedRows = pStmt.executeUpdate(); - this.connection.commit(); - - if(updatedRows < 1){ - throw new DataCommandException("Fail executing. " + pStmt.toString()); - } - - } catch (SQLException e) { - if(this.connection != null){ - try { - this.connection.rollback(); - } catch (SQLException e1) { - e1.printStackTrace(); - throw new DataCommandException(e.getMessage()); - } - throw new DataCommandException(e.getMessage()); - } - } finally{ - try { - if(pStmt != null) pStmt.close(); - this.connection.setAutoCommit(true); - - } catch (SQLException e1) { - throw new DataCommandException(e1.getMessage()); - } - } - } - - - /** - * The subclass should provide a method to prepare Insert, Update or Delete - * @return {@link PreparedStatement} - * @throws SQLException - */ - protected abstract PreparedStatement prepareStatement() throws SQLException; - - -} diff --git a/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/DataCommand.java b/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/DataCommand.java deleted file mode 100644 index 04c13d23b7..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/DataCommand.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * - */ -package org.georchestra.lib.sqlcommand; - -import java.sql.Connection; - -/** - * @author Mauricio Pazos - * - */ -public interface DataCommand { - - /** - * Database connection - * @param connection - */ - public void setConnection(Connection connection); - - /** - * Execute the sql command specified - * @throws DataCommandException - */ - public void execute() throws DataCommandException; - -} diff --git a/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/DataCommandException.java b/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/DataCommandException.java deleted file mode 100644 index 737d0298d9..0000000000 --- a/ldapadmin/src/main/java/org/georchestra/lib/sqlcommand/DataCommandException.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.georchestra.lib.sqlcommand; - -import java.sql.SQLException; - -public class DataCommandException extends Exception { - - /** - * for serialization - */ - private static final long serialVersionUID = -5196425322579527757L; - - - public DataCommandException(String message) { - super(message); - } - - - public DataCommandException(SQLException e) { - super(e); - } - - - -} diff --git a/ldapadmin/src/main/resources/META-INF/spring/applicationContext.xml b/ldapadmin/src/main/resources/META-INF/spring/applicationContext.xml deleted file mode 100644 index 7bb76c070b..0000000000 --- a/ldapadmin/src/main/resources/META-INF/spring/applicationContext.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/ldapadmin/src/main/webapp/WEB-INF/classes/log4j.properties b/ldapadmin/src/main/webapp/WEB-INF/classes/log4j.properties deleted file mode 100644 index e85a47dcf5..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/classes/log4j.properties +++ /dev/null @@ -1,28 +0,0 @@ -#------------------------------------------------------------------------------ -# -# The following properties set the logging levels and log appender. The -# log4j.rootLogger variable defines the default log level and one or more -# appenders. For the console, use 'S'. For the daily rolling file, use 'R'. -# For an HTML formatted log, use 'H'. -# -# To override the default (rootLogger) log level, define a property of the -# form (see below for available values): -# -# log4j.logger. = -# -# Possible Log Levels: -# FATAL, ERROR, WARN, INFO, DEBUG -# -#------------------------------------------------------------------------------ -log4j.rootLogger=WARN, R - -log4j.logger.org.georchestra.ldapadmin=WARN, R -log4j.logger.org.georchestra.ldapadmin.ws.utils=INFO, R - -log4j.appender.R = org.apache.log4j.rolling.RollingFileAppender -log4j.appender.R.RollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy -log4j.appender.R.RollingPolicy.FileNamePattern = /tmp/ldapadmin.%d.log.gz -log4j.appender.R.RollingPolicy.ActiveFileName = /tmp/ldapadmin.log -log4j.appender.R.Append = true -log4j.appender.R.layout = org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n diff --git a/ldapadmin/src/main/webapp/WEB-INF/i18n/application.properties b/ldapadmin/src/main/webapp/WEB-INF/i18n/application.properties deleted file mode 100644 index 0c743c51e3..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/i18n/application.properties +++ /dev/null @@ -1,111 +0,0 @@ -#Updated at Thu Jun 13 21:39:28 CEST 2013 -#Thu Jun 13 21:39:28 CEST 2013 - -### General - -submit.label=Submit -form.error=The form contains errors - -### Forms - -changePasswordForm.title=Change password. -changePasswordForm.subtitle=Enter your new password. -changePasswordForm.fieldset.password=Password -changePasswordForm.success=Password updated successfully -createAccountForm.title=New account. -createAccountForm.subtitle=Create your account. -createAccountForm.description=An email will be sent to the above address to confirm the account creation. -createAccountForm.fieldset.userDetails=User details -createAccountForm.fieldset.credentials=Credentials -createAccountForm.fieldset.reCaptcha=ReCaptcha verification -editUserDetailsForm.title=User details. -editUserDetailsForm.subtitle=Update your account. -editUserDetailsForm.description=In this page you can manage your account informations and modify your password. -editUserDetailsForm.fieldset.userDetails=User details -editUserDetailsForm.fieldset.credentials=Credentials -editUserDetailsForm.changePassword.link=Change Password -editUserDetailsForm.success=User details updated successfully -passwordRecoveryForm.title=Ask for a new password. -passwordRecoveryForm.subtitle=You've lost your password or your account was just created. -passwordRecoveryForm.description=You will receive an email at the following address with a reset link to let you set a new password. -passwordRecoveryForm.fieldset.email=Get new password -passwordRecoveryForm.fieldset.reCaptcha=ReCaptcha verification -newPasswordForm.title=Reset password. -newPasswordForm.subtitle=Enter a new password. -newPasswordForm.description=You requested a new password. Please enter your new password. -newPasswordForm.fieldset.password=Password -newPasswordForm.success=Password updated successfully - -### Other JSP pages - -dataAccessFailure.title=Data access failure -emailWasSent.body=An e-mail was sent with a link for updating password. Please, check your e-mail box. -emailWasSent.link=Home -emailWasSent.title=E-mail sent -forbidden.body=Sorry, but you don't have access to this area. -forbidden.link=Home -forbidden.title=Forbidden -newUserWelcome.body=Your request has been submitted and should be processed in the next hours. Watch your e-mail box. -newUserWelcome.link=Log in -newUserWelcome.title=Request submitted -passwordUpdated.body=The password has been successfully updated. You will now be able to log in. -passwordUpdated.link=Log in -passwordUpdated.title=Password updated -resourceNotFound.title=Resource not found -uncaughtException.title=Internal Error - -### Form fields properties - -confirmPassword.label=Confirm password -confirmPassword.placeholder=Confirm password -confirmPassword.error.required=Required -confirmPassword.error.pwdNotEquals=These passwords don\'t match -confirmPassword.error.pwdNotEquals.tag=Mismatch -description.label=Description -description.placeholder=Description -email.label=E-mail -email.placeholder=E-mail -email.error.required=Required -email.error.invalidFormat=Invalid format -email.error.exist=This e-mail is already in use -email.error.notFound=No user is registered with this email. -error.required=Required -facsimile.label=Fax -facsimile.placeholder=Fax -firstName.label=First Name -firstName.placeholder=First Name -firstName.error.required=Required -org.label=Organization -org.placeholder=Organization -password.label=Password -password.placeholder=Password -password.error.required=Required -password.error.sizeError=The password must contain at least 8 characters -password.label.empty=empty -password.label.good=good -password.label.strong=strong -password.label.veryweak=very weak -password.label.weak=weak -postalAddress.label=Postal address -postalAddress.placeholder=Postal address -phone.label=Phone -phone.placeholder=Phone -phone.error.nonNumeric=The phone number should only contain digits -recaptcha_response_field.error.required=Required -recaptcha_response_field.error.captchaNoMatch=The texts didn't match -recaptcha.incorrect=Incorrect, please try again -recaptcha.words=Enter the words above -recaptcha.numbers=Enter the numbers you hear -recaptcha.audio=Get an audio CAPTCHA -recaptcha.image=Get an image CAPTCHA -recaptcha.noscript.title=Activate Javacript for a better interface -surname.label=Surname -surname.placeholder=Surname -surname.error.required=Required -title.label=Title -title.placeholder=Title -uid.label=User ID -uid.placeholder=User ID -uid.error.required=user identifier is required -uid.error.exist=This identifier has already been selected. Please choose another one. -uid.error.invalid=Invalid uid. It must begin with a letter followed by one or more letters, digits, \"-\" (hyphens) or \".\" (dots) diff --git a/ldapadmin/src/main/webapp/WEB-INF/i18n/application_de.properties b/ldapadmin/src/main/webapp/WEB-INF/i18n/application_de.properties deleted file mode 100644 index 49754be607..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/i18n/application_de.properties +++ /dev/null @@ -1,111 +0,0 @@ -#Updated at Thu Jun 13 21:39:28 CEST 2013 -#Thu Jun 13 21:39:28 CEST 2013 - -### General - -submit.label=Senden -form.error=Das Formular enthält Fehler - -### Forms - -changePasswordForm.title=Passwort ändern. -changePasswordForm.subtitle=Geben Sie Ihr neues Passwort ein. -changePasswordForm.fieldset.password=Passwort -changePasswordForm.success=Passwort erfolgreich aktualisiert -createAccountForm.title=Neues Konto. -createAccountForm.subtitle=Erstellen Sie Ihr Konto. -createAccountForm.description=Eine E-Mail wird an die Adresse geschickt, um die Account-Erstellung zu bestätigen. -createAccountForm.fieldset.userDetails=Benutzerdetails -createAccountForm.fieldset.credentials=Credentials -createAccountForm.fieldset.reCaptcha=ReCaptcha Überprüfung -editUserDetailsForm.title=Benutzerdetails. -editUserDetailsForm.subtitle=Aktualisieren Sie Ihr Konto. -editUserDetailsForm.description=Auf dieser Seite können Sie Ihre Kontoinformationen verwalten und Ihr Passwort ändern. -editUserDetailsForm.fieldset.userDetails=Benutzerdetails -editUserDetailsForm.fieldset.credentials=Credentials -editUserDetailsForm.changePassword.link=Passwort ändern -editUserDetailsForm.success=Benutzerdetails erfolgreich aktualisiert -passwordRecoveryForm.title=Ein neues Password anfordern. -passwordRecoveryForm.subtitle=Sie haben Ihr Passwort vergessen oder Ihr Konto wurde gerade erst erstellt. -passwordRecoveryForm.description=Wir senden Ihnen eine E-Mail an die folgende Adresse mit einem Link um das Password zurückzusetzen. -passwordRecoveryForm.fieldset.email=Neues Passwort anfordern -passwordRecoveryForm.fieldset.reCaptcha=ReCaptcha Überprüfung -newPasswordForm.title=Passwort zurücksetzen. -newPasswordForm.subtitle=Ein neues Passwort eingeben. -newPasswordForm.description=Bitte geben Sie Ihr neues Passwort ein. -newPasswordForm.fieldset.password=Passwort -newPasswordForm.success=Passwort erfolgreich aktualisiert - -### Other JSP pages - -dataAccessFailure.title=Datenzugriffsfehler -emailWasSent.body=Eine E-Mail mit einem Link zum Zurücksetzen des Passworts wurde versendet. Bitte überprüfen Sie Ihren Posteingang. -emailWasSent.link=Home -emailWasSent.title=E-Mail gesendet -forbidden.body=Entschuldigung, aber Sie haben keinen Zugang zu diesem Bereich. -forbidden.link=Home -forbidden.title=Geschützter Bereich -newUserWelcome.body=Ihre Anfrage wurde eingereicht und sollte in den nächsten Stunden bearbeitet werden. Kontrollieren Sie bitte Ihren Posteingang. -newUserWelcome.link=Anmelden -newUserWelcome.title=Anfrage abgesendet -passwordUpdated.body=Das Password wurde erfolgreich geändert. Sie können sich nun einloggen. -passwordUpdated.link=Anmelden -passwordUpdated.title=Passwort aktualisiert -resourceNotFound.title=Ressource wurde nicht gefunden -uncaughtException.title=Interner Fehler - -### Form fields properties - -confirmPassword.label=Passwort bestätigen -confirmPassword.placeholder=Passwort bestätigen -confirmPassword.error.required=Notwendig -confirmPassword.error.pwdNotEquals=Passwörter stimmen nicht überein -confirmPassword.error.pwdNotEquals.tag=Nicht das gleiche Passwort -description.label=Beschreibung -description.placeholder=Beschreibung -email.label=E-Mail -email.placeholder=E-Mail -email.error.required=Notwendig -email.error.invalidFormat=Ungültiges Format -email.error.exist=Es existiert bereits ein Account mit dieser E-Mail Adresse. -email.error.notFound=Kein Benutzer ist mit dieser E-Mail registriert. -error.required=Notwendig -facsimile.label=Fax -facsimile.placeholder=Fax -firstName.label=Vorname -firstName.placeholder=Vorname -firstName.error.required=Notwendig -org.label=Organisation -org.placeholder=Organisation -password.label=Passwort -password.placeholder=Passwort -password.error.required=Notwendig -password.error.sizeError=Das Passwort muss mindestens 8 Zeichen enthalten -password.label.empty=leer -password.label.good=gut -password.label.strong=stark -password.label.veryweak=sehr schwach -password.label.weak=schwach -postalAddress.label=Postanschrift -postalAddress.placeholder=Postanschrift -phone.label=Telefon -phone.placeholder=Telefon -phone.error.nonNumeric=Die Telefonnummer sollte nur Ziffern enthalten -recaptcha_response_field.error.required=Notwendig -recaptcha_response_field.error.captchaNoMatch=Die Texte stimmen nicht überein -recaptcha.incorrect=Falsch, bitte versuchen es noch einmal -recaptcha.words=Bitte geben Sie die oben angezeigten Wörter ein. -recaptcha.numbers=Bitte geben Sie die Nummern ein, die Sie hören. -recaptcha.audio=In den Audio-Modus wechseln. -recaptcha.image=In den Graphik-Modus wechseln. -recaptcha.noscript.title=Aktivieren Sie Javacript für eine optimale Verwendung. -surname.label=Name -surname.placeholder=Name -surname.error.required=Notwendig -title.label=Titel -title.placeholder=Titel -uid.label=Benutzer-ID -uid.placeholder=Benutzer-ID -uid.error.required=Benutzer-ID ist notwendig -uid.error.exist=Dieses Benutzer-ID ist bereits ausgewählt. Bitte wählen Sie einen anderen. -uid.error.invalid=Ungültige UID. Die UID muss mit einem Buchstaben beginnen, gefolgt von einem oder mehreren Buchstaben, Ziffern, \"-\" (Bindestrich) oder \".\" (Punkte). \ No newline at end of file diff --git a/ldapadmin/src/main/webapp/WEB-INF/i18n/application_es.properties b/ldapadmin/src/main/webapp/WEB-INF/i18n/application_es.properties deleted file mode 100644 index 64e419ee5e..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/i18n/application_es.properties +++ /dev/null @@ -1,111 +0,0 @@ -#Updated at Thu Jun 13 21:39:28 CEST 2013 -#Thu Jun 13 21:39:28 CEST 2013 - -### General - -submit.label=Enviar -form.error=El formulario tiene errores - -### Forms - -changePasswordForm.title=Cambio de clave. -changePasswordForm.subtitle=Ingrese su nueva clave. -changePasswordForm.fieldset.password=Clave -changePasswordForm.success=Clave actualizada con éxito -createAccountForm.title=Nueva cuenta. -createAccountForm.subtitle=Solicite la creación de una cuenta. -createAccountForm.description=Un correo de confirmación será mandado a la dirección indicada. -createAccountForm.fieldset.userDetails=Detalles del usuario -createAccountForm.fieldset.credentials=Credenciales -createAccountForm.fieldset.reCaptcha=Verificación ReCaptcha -editUserDetailsForm.title=Detalles de la cuenta. -editUserDetailsForm.subtitle=Actualice sus informaciones. -editUserDetailsForm.description=En esta página, puede manejar las informaciones de su cuenta y modificar su clave. -editUserDetailsForm.fieldset.userDetails=Detalles del usuario -editUserDetailsForm.fieldset.credentials=Credenciales -editUserDetailsForm.changePassword.link=Cambiar clave -editUserDetailsForm.success=Detalles actualizados con éxito -passwordRecoveryForm.title=Solicite una clave. -passwordRecoveryForm.subtitle=Ha perdido su contraseña o su cuenta ha sido creada. -passwordRecoveryForm.description=Recibirá un mail a la dirección siguiente con un vínculo para establecer una nueva clave de su elección. -passwordRecoveryForm.fieldset.email=Obtener una nueva clave -passwordRecoveryForm.fieldset.reCaptcha=Verificación ReCaptcha -newPasswordForm.title=Recuperación de clave. -newPasswordForm.subtitle=Ingrese una nueva clave. -newPasswordForm.description=Usted le pedirá que cree una nueva contraseña. Por favor ingrese su nueva contraseña. -newPasswordForm.fieldset.password=Clave -newPasswordForm.success=Clave actualizada con éxito - -### Other JSP pages - -dataAccessFailure.title=Error de acceso a los datos -emailWasSent.body=Un e-mail ha sido enviado con un vínculo para actualizar la nueva clave. Por favor, revise su e-mail. -emailWasSent.link=Home -emailWasSent.title=E-mail enviado -forbidden.body=No tiene acceso a esta sección. -forbidden.link=Ir al inicio -forbidden.title=Prohibido -newUserWelcome.body=Su solicitud ha sido enviada y será procesada en las proximas horas. Controle su e-mail. -newUserWelcome.link=Conectarse -newUserWelcome.title=Solicitud enviada -passwordUpdated.body=La clave fue actualizada con éxito. Puede conectarse ahora. -passwordUpdated.link=Conectarse -passwordUpdated.title=Clave actualizada -resourceNotFound.title=Recurso no encontrado -uncaughtException.title=Error interna - -### Form fields properties - -confirmPassword.label=Confirmar clave -confirmPassword.placeholder=Confirmar clave -confirmPassword.error.required=Requerida -confirmPassword.error.pwdNotEquals=Las claves son diferentes -confirmPassword.error.pwdNotEquals.tag=Diferentes -description.label=Descripción -description.placeholder=Descripción -email.label=e-mail -email.placeholder=e-mail -email.error.required=Requerido -email.error.invalidFormat=Formato inválido -email.error.exist=Este mail ya esta utilizado. -email.error.notFound=Ningún usuario esta registrado con este e-mail. -error.required=Requerido -facsimile.label=Fax -facsimile.placeholder=Fax -firstName.label=Nombre -firstName.placeholder=Nombre -firstName.error.required=Requerido -org.label=Organización -org.placeholder=Organización -password.label=Clave -password.placeholder=Clave -password.error.required=Requerida -password.error.sizeError=La clave debe tener al menos 8 carácteres -password.label.empty=vacío -password.label.good=bueno -password.label.strong=fuerte -password.label.veryweak=muy debíl -password.label.weak=debíl -phone.label=Teléfono -phone.placeholder=Teléfono -phone.error.nonNumeric=El número de teléfono solo puede contener dígitos -postalAddress.label=Dirección postal -postalAddress.placeholder=Dirección postal -recaptcha_response_field.error.required=Requerido -recaptcha_response_field.error.captchaNoMatch=El texto ingresado no coincide. -recaptcha.incorrect=Incorrecto, por favor pruebe de nuevo -recaptcha.words=Ingrese las palabras de la imagen -recaptcha.numbers=Ingrese los números que escucha -recaptcha.audio=Obtener un CAPTCHA audio -recaptcha.image=Obtener una imagen CAPTCHA -recaptcha.noscript.title=Active Javacript para beneficiar de una mejor interfaz -surname.label=Apellido -surname.placeholder=Apellido -surname.error.required=Requerido -title.label=Función -title.placeholder=Función -uid.label=ID Usuario -uid.placeholder=ID Usuario -uid.error.required=Requerido -uid.error.exist=Este identificador ya fue seleccionado. Por favor elija otro. -uid.error.invalid=uid invalido. Debe comenzar por una letra y estar seguido de uno o más letras, dígitos, guión (\"-\") o puntos (\".\") diff --git a/ldapadmin/src/main/webapp/WEB-INF/i18n/application_fr.properties b/ldapadmin/src/main/webapp/WEB-INF/i18n/application_fr.properties deleted file mode 100644 index 9e44199a0f..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/i18n/application_fr.properties +++ /dev/null @@ -1,111 +0,0 @@ -#Updated at Thu Jun 13 21:39:28 CEST 2013 -#Thu Jun 13 21:39:28 CEST 2013 - -### General - -submit.label=Envoyer -form.error=Le formulaire contient des erreurs - -### Forms - -changePasswordForm.title=Changement de mot de passe. -changePasswordForm.subtitle=Entrez votre nouveau mot de passe. -changePasswordForm.fieldset.password=Mot de passe -changePasswordForm.success=Mot de passe actualisé -createAccountForm.title=Nouveau compte. -createAccountForm.subtitle=Sollicitez la création d'un compte. -createAccountForm.description=Un courriel de confirmation sera envoyé à l'adresse indiquée. -createAccountForm.fieldset.userDetails=Détails du compte -createAccountForm.fieldset.credentials=Identification -createAccountForm.fieldset.reCaptcha=Vérification ReCaptcha -editUserDetailsForm.title=Détails du compte. -editUserDetailsForm.subtitle=Actualisez vos informations. -editUserDetailsForm.description=Sur cette page vous pouvez gérer les informations de votre compte, et modifier votre mot de passe. -editUserDetailsForm.fieldset.userDetails=Détails du compte -editUserDetailsForm.fieldset.credentials=Identification -editUserDetailsForm.changePassword.link=Changer le mot de passe -editUserDetailsForm.success=Compte actualisé -passwordRecoveryForm.title=Demande de mot de passe. -passwordRecoveryForm.subtitle=Vous avez perdu votre mot de passe ou votre compte a été créé. -passwordRecoveryForm.description=Vous recevrez par courriel un lien vous permettant de définir votre mot de passe. -passwordRecoveryForm.fieldset.email=Obtenir un nouveau mot de passe -passwordRecoveryForm.fieldset.reCaptcha=Vérification ReCaptcha -newPasswordForm.title=Récupération du mot de passe. -newPasswordForm.subtitle=Entrez un nouveau mot de passe. -newPasswordForm.description=Vous avez demandé la création d'un nouveau mot de passe. Veuillez saisir votre nouveau mot de passe. -newPasswordForm.fieldset.password=Mot de passe -newPasswordForm.success=Mot de passe actualisé - -### Other JSP pages - -dataAccessFailure.title=Erreur d'accès aux données -emailWasSent.body=Un courriel a été envoyé à l'instant pour vous permettre de changer votre mot de passe. -emailWasSent.link=Portail -emailWasSent.title=Courriel envoyé -forbidden.body=Désolé, vous n'avez pas accès à cette page. -forbidden.link=Portail -forbidden.title=Accès interdit -newUserWelcome.body=Votre demande d'inscription a été prise en compte et sera traitée dans les meilleurs délais. -newUserWelcome.link=Se connecter -newUserWelcome.title=Demande d'inscription prise en compte -passwordUpdated.body=Le mot de passe a été mis à jour. Vous pouvez maintenant vous connecter. -passwordUpdated.link=Se connecter -passwordUpdated.title=Mot de passe mis à jour -resourceNotFound.title=Ressource introuvable -uncaughtException.title=Erreur interne - -### Form fields properties - -confirmPassword.label=Confirmation du mot de passe -confirmPassword.placeholder=Confirmation du mot de passe -confirmPassword.error.required=Requis -confirmPassword.error.pwdNotEquals=Les mots de passe sont différents -confirmPassword.error.pwdNotEquals.tag=Différents -description.label=Description -description.placeholder=Description -email.label=Courriel -email.placeholder=Courriel -email.error.required=Requis -email.error.invalidFormat=Format invalide -email.error.exist=Ce courriel est déjà utilisé -email.error.notFound=Aucun utilisateur n'est enregistré avec ce courriel. -error.required=Requis -facsimile.label=Fax -facsimile.placeholder=Fax -firstName.label=Prénom -firstName.placeholder=Prénom -firstName.error.required=Requis -org.label=Organisme -org.placeholder=Organisme -password.label=Mot de passe -password.placeholder=Mot de passe -password.error.required=Requis -password.error.sizeError=Le mot de passe doit contenir au moins 8 caractères -password.label.empty=vide -password.label.good=bon -password.label.strong=fort -password.label.veryweak=très faible -password.label.weak=faible -postalAddress.label=Adresse postale -postalAddress.placeholder=Adresse postale -phone.label=Téléphone -phone.placeholder=Téléphone -phone.error.nonNumeric=Le numéro de téléphone doit contenir uniquement des chiffres -recaptcha_response_field.error.required=Requis -recaptcha_response_field.error.captchaNoMatch=Les textes ne correspondent pas -recaptcha.incorrect=Erreur. Réessayez -recaptcha.words=Entrez les mots ci-dessus -recaptcha.numbers=Entrez les nombres que vous entendez -recaptcha.audio=Obtenir un CAPTCHA sous forme audio -recaptcha.image=Obtenir un CAPTCHA sous forme d'image -recaptcha.noscript.title=Activez Javascript pour une meilleure interface -surname.label=Nom -surname.placeholder=Nom -surname.error.required=Requis -title.label=Fonction -title.placeholder=Fonction -uid.label=Login -uid.placeholder=Login -uid.error.required=Requis -uid.error.exist=Ce login est déjà utilisé. Choisissez-en un autre. -uid.error.invalid=Login invalide. Il doit commencer par une lettre et être suivi par un ou plusieurs chiffres, lettres, tirets ou points. diff --git a/ldapadmin/src/main/webapp/WEB-INF/spring/webmvc-config.xml b/ldapadmin/src/main/webapp/WEB-INF/spring/webmvc-config.xml deleted file mode 100644 index 7d8cdfabc4..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/spring/webmvc-config.xml +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Add one or more user identifiers (uid) of protected user - - geoserver_privileged_user - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - org.hibernate.dialect.PostgreSQL94Dialect - false - false - update - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - dataAccessFailure - resourceNotFound - resourceNotFound - resourceNotFound - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ldapadmin/src/main/webapp/WEB-INF/tags/recaptcha.tag b/ldapadmin/src/main/webapp/WEB-INF/tags/recaptcha.tag deleted file mode 100644 index 7f38918ee2..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/tags/recaptcha.tag +++ /dev/null @@ -1,55 +0,0 @@ -<%@tag description="Extended recaptcha tag to allow for sophisticated errors" pageEncoding="UTF-8"%> -<%@taglib prefix="s" uri="http://www.springframework.org/tags" %> -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@attribute name="path" required="true" type="java.lang.String"%> -<%@attribute name="spanId" required="false" type="java.lang.String"%> - - - - - - - - - diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/createAccountForm.jsp b/ldapadmin/src/main/webapp/WEB-INF/views/createAccountForm.jsp deleted file mode 100644 index 2d74f4914e..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/views/createAccountForm.jsp +++ /dev/null @@ -1,177 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" - pageEncoding="UTF-8"%> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> -<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> -<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> -<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %> - -<%@ page import="net.tanesha.recaptcha.ReCaptcha" %> -<%@ page import="net.tanesha.recaptcha.ReCaptchaFactory" %> - - - - - - - - - - <s:message code="createAccountForm.title"/> - - - - - <%@ include file="header.jsp" %> - -
- -

- - - -
- - ${message} -
-
- - - -
- - -
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - - - - - - - -
- -
- - -
- -
-
-
- -
-
-
- -
- - - <%@ include file="validation.jsp" %> - - - diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/dataAccessFailure.jsp b/ldapadmin/src/main/webapp/WEB-INF/views/dataAccessFailure.jsp deleted file mode 100644 index b3b65823fb..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/views/dataAccessFailure.jsp +++ /dev/null @@ -1,42 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" - pageEncoding="UTF-8"%> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> -<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> -<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> - - - - - - - - - - <s:message code="dataAccessFailure.title"/> - - - - - <%@ include file="header.jsp" %> - -
- -<% -Exception ex = (Exception) request.getAttribute("exception"); -%> -
-<%= ex.getMessage() %>
-		
-
-<%
-ex.printStackTrace(new java.io.PrintWriter(out));
-%>
-		
-
- - - - diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/editUserDetailsForm.jsp b/ldapadmin/src/main/webapp/WEB-INF/views/editUserDetailsForm.jsp deleted file mode 100644 index e2a3cf509d..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/views/editUserDetailsForm.jsp +++ /dev/null @@ -1,146 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" - pageEncoding="UTF-8"%> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> -<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> -<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> -<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %> - - - - - - - - - - <s:message code="editUserDetailsForm.title"/> - - - - - <%@ include file="header.jsp" %> - -
- -

- - - -
- - -
-
- - - -
- - -
-
-
- -
- - - - - - - -
- -
-

- ${editUserDetailsFormBean.email} -

-
-
- - - - - - - - - - - - - - - - - - -
- -
- -
- -
-

- ${editUserDetailsFormBean.uid} -

-
-
-
- -
-

- - - -

-
-
-
- -
-
-
- -
-
-
- - - - - <%@ include file="validation.jsp" %> - - - diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/emailWasSent.jsp b/ldapadmin/src/main/webapp/WEB-INF/views/emailWasSent.jsp deleted file mode 100644 index 3617b88631..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/views/emailWasSent.jsp +++ /dev/null @@ -1,28 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> - - - - - - - - - - <s:message code="emailWasSent.title" /> - - - - <%@ include file="header.jsp" %> - -
-
-

-

-
-
- - - - diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/forbidden.jsp b/ldapadmin/src/main/webapp/WEB-INF/views/forbidden.jsp deleted file mode 100644 index 788e2923db..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/views/forbidden.jsp +++ /dev/null @@ -1,29 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> - - - - - - - - - - - <s:message code="forbidden.title" /> - - - - <%@ include file="header.jsp" %> - -
-
-

-

-
-
- - - - diff --git a/ldapadmin/src/main/webapp/WEB-INF/views/passwordRecoveryForm.jsp b/ldapadmin/src/main/webapp/WEB-INF/views/passwordRecoveryForm.jsp deleted file mode 100644 index 9c9441a4fc..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/views/passwordRecoveryForm.jsp +++ /dev/null @@ -1,91 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" - pageEncoding="UTF-8"%> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> -<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> -<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> -<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %> - -<%@ page import="net.tanesha.recaptcha.ReCaptcha" %> -<%@ page import="net.tanesha.recaptcha.ReCaptchaFactory" %> - - - - - - - - - - <s:message code="passwordRecoveryForm.title"/> - - - - <%@ include file="header.jsp" %> - -
- -

- - -
- - ${message} -
-
- - - -
- - -
-
-
- -
- - - - -
- -
- - -
- -
-
-
- -
-
-
- - - - - <%@ include file="validation.jsp" %> - - - diff --git a/ldapadmin/src/main/webapp/WEB-INF/web.xml b/ldapadmin/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 069b912ee7..0000000000 --- a/ldapadmin/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - ldapadmin - - ldapadmin application - - - - - contextConfigLocation - classpath*:META-INF/spring/applicationContext*.xml - - - - - org.springframework.web.context.ContextLoaderListener - - - - - ldapadmin - org.springframework.web.servlet.DispatcherServlet - - contextConfigLocation - WEB-INF/spring/webmvc-config.xml - - 1 - - - - ldapadmin - / - /privateui/ - - - - default - /privateui/css/* - /privateui/js/* - /privateui/lib/* - /privateui/partials/* - /account/css/* - /account/fonts/* - /account/js/* - - - - 10 - - - - java.lang.Exception - /WEB-INF/views/uncaughtException.jsp - - - 404 - /WEB-INF/views/resourceNotFound.jsp - - - 403 - /WEB-INF/views/forbidden.jsp - - - - characterEncodingFilter - org.springframework.web.filter.CharacterEncodingFilter - - encoding - UTF-8 - - - forceEncoding - true - - - - characterEncodingFilter - /* - - - - - - - diff --git a/ldapadmin/src/main/webapp/account/css/ldapadmin.css b/ldapadmin/src/main/webapp/account/css/ldapadmin.css deleted file mode 100644 index 0823c33536..0000000000 --- a/ldapadmin/src/main/webapp/account/css/ldapadmin.css +++ /dev/null @@ -1,14 +0,0 @@ -/* Keep the labels white, even when an error occurred */ -.has-error .label { - color: white; -} - -/* Center repatcha image */ -#recaptcha_image img { - margin-left: auto; - margin-right: auto; -} -/* Fix 2px border issue */ -#recaptcha_response_field .input-group-addon { - border-left: 0 none; -} diff --git a/ldapadmin/src/main/webapp/privateui/css/.gitkeep b/ldapadmin/src/main/webapp/privateui/css/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ldapadmin/src/main/webapp/privateui/css/main.css b/ldapadmin/src/main/webapp/privateui/css/main.css deleted file mode 100644 index 7c89913c60..0000000000 --- a/ldapadmin/src/main/webapp/privateui/css/main.css +++ /dev/null @@ -1,114 +0,0 @@ -#container>.row-fluid { - height: 100%; -} -#content { - height: 100%; - position: relative; -} -#sidebar { - height: 100%; - position: relative; -} -#sidebar .toolbar { - padding-top: 42px; -} -#sidebar>div { - padding-left: 10px; -} -#sidebar .content { - overflow: auto; - width: 100%; - top: 84px; - bottom: 0; - position: absolute; -} -#sidebar a { - color: black; -} -#sidebar a.active { - font-weight: bold; - color: #08c; -} -.alert { - position: absolute; -} -.alert.in { - display: block; -} - -#users { - width: 100%; - position: absolute; - top: 76px; - bottom: 0; - overflow: auto; - z-index: 0; -} - -#users .table td { - border-top: 0; - border-bottom: 1px solid #ddd; -} -#noone { - margin-top: 40px; -} - -.toolbar { - height: 30px; - line-height: 20px; - padding-bottom: 4px; - padding-top: 4px; -} -.toolbar .checkbox-wrap { - padding: 4px 20px 4px 4px; - display: inline-block; -} -.toolbar.shadow { - box-shadow: 0 10px 10px -10px grey; - position: relative; - z-index: 1; -} -.groups li a i { - display: inline-block; - width: 14px; -} -.dropdown-menu>li>a.group { - padding-left: 3px; -} - -.form-fixed { - width: 600px; -} - -.form-horizontal .control-label { - width: 90px; -} -.form-horizontal .controls { - margin-left: 110px; -} - -.accordion { - margin: 0; -} -.accordion-group { - border: none; -} -.accordion-heading .accordion-toggle { - padding: 0; -} -.accordion-body { - padding-left: 10px; -} -.accordion-inner { - border: none; - padding: 0; -} - -i.icon-blank { - display: inline-block; - width: 14px; - height: 14px; - margin-top: 1px; - line-height: 14px; - vertical-align: text-top; -} diff --git a/ldapadmin/src/main/webapp/privateui/data/users/all.json b/ldapadmin/src/main/webapp/privateui/data/users/all.json deleted file mode 100644 index e271e57e09..0000000000 --- a/ldapadmin/src/main/webapp/privateui/data/users/all.json +++ /dev/null @@ -1,976 +0,0 @@ -[ - { - "id": 0, - "picture": "http://placehold.it/32x32", - "name": "Patrica Barton", - "company": "Tellifly", - "email": "patricabarton@tellifly.com", - "group": [] - }, - { - "id": 1, - "picture": "http://placehold.it/32x32", - "name": "James Mcgee", - "company": "Blurrybus", - "email": "jamesmcgee@blurrybus.com", - "group": [] - }, - { - "id": 2, - "picture": "http://placehold.it/32x32", - "name": "Britt Kerr", - "company": "Callflex", - "email": "brittkerr@callflex.com", - "group": [] - }, - { - "id": 3, - "picture": "http://placehold.it/32x32", - "name": "Arlene Conrad", - "company": "Xerex", - "email": "arleneconrad@xerex.com", - "group": [] - }, - { - "id": 4, - "picture": "http://placehold.it/32x32", - "name": "Stein Vaughn", - "company": "Coash", - "email": "steinvaughn@coash.com", - "group": [ - "EL_YYY", - "Administrator" - ] - }, - { - "id": 5, - "picture": "http://placehold.it/32x32", - "name": "Gamble Mcknight", - "company": "Opportech", - "email": "gamblemcknight@opportech.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 6, - "picture": "http://placehold.it/32x32", - "name": "Gibson Harper", - "company": "Corepan", - "email": "gibsonharper@corepan.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 7, - "picture": "http://placehold.it/32x32", - "name": "Angeline Baird", - "company": "Nikuda", - "email": "angelinebaird@nikuda.com", - "group": [] - }, - { - "id": 8, - "picture": "http://placehold.it/32x32", - "name": "Tisha Ellis", - "company": "Megall", - "email": "tishaellis@megall.com", - "group": [ - "EL_XXX" - ] - }, - { - "id": 9, - "picture": "http://placehold.it/32x32", - "name": "Lowery Soto", - "company": "Geekol", - "email": "lowerysoto@geekol.com", - "group": [ - "SV_YYY", - "EL_YYY" - ] - }, - { - "id": 10, - "picture": "http://placehold.it/32x32", - "name": "Jennie Munoz", - "company": "Prowaste", - "email": "jenniemunoz@prowaste.com", - "group": [ - "EL_XXX", - "EL_YYY" - ] - }, - { - "id": 11, - "picture": "http://placehold.it/32x32", - "name": "Michele Potter", - "company": "Genekom", - "email": "michelepotter@genekom.com", - "group": [ - "EL_XXX", - "SV_YYY" - ] - }, - { - "id": 12, - "picture": "http://placehold.it/32x32", - "name": "Alejandra Richmond", - "company": "Qnekt", - "email": "alejandrarichmond@qnekt.com", - "group": [ - "EL_XXX" - ] - }, - { - "id": 13, - "picture": "http://placehold.it/32x32", - "name": "Janet Walls", - "company": "Veraq", - "email": "janetwalls@veraq.com", - "group": [] - }, - { - "id": 14, - "picture": "http://placehold.it/32x32", - "name": "Ford Mcdowell", - "company": "Rameon", - "email": "fordmcdowell@rameon.com", - "group": [] - }, - { - "id": 15, - "picture": "http://placehold.it/32x32", - "name": "Justine Cherry", - "company": "Dreamia", - "email": "justinecherry@dreamia.com", - "group": [ - "EL_YYY", - "EL_XXX" - ] - }, - { - "id": 16, - "picture": "http://placehold.it/32x32", - "name": "Sallie Crane", - "company": "Futurize", - "email": "salliecrane@futurize.com", - "group": [] - }, - { - "id": 17, - "picture": "http://placehold.it/32x32", - "name": "Flores Valencia", - "company": "Zenolux", - "email": "floresvalencia@zenolux.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 18, - "picture": "http://placehold.it/32x32", - "name": "Alford Avery", - "company": "Urbanshee", - "email": "alfordavery@urbanshee.com", - "group": [] - }, - { - "id": 19, - "picture": "http://placehold.it/32x32", - "name": "Gena Mosley", - "company": "Crustatia", - "email": "genamosley@crustatia.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 20, - "picture": "http://placehold.it/32x32", - "name": "Bonnie Butler", - "company": "Decratex", - "email": "bonniebutler@decratex.com", - "group": [ - "SV_XXX", - "SV_YYY" - ] - }, - { - "id": 21, - "picture": "http://placehold.it/32x32", - "name": "Lorie Long", - "company": "Kage", - "email": "lorielong@kage.com", - "group": [ - "EL_XXX", - "Administrator" - ] - }, - { - "id": 22, - "picture": "http://placehold.it/32x32", - "name": "Shelia Leblanc", - "company": "Softmicro", - "email": "shelialeblanc@softmicro.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 23, - "picture": "http://placehold.it/32x32", - "name": "Carson Norton", - "company": "Realmo", - "email": "carsonnorton@realmo.com", - "group": [] - }, - { - "id": 24, - "picture": "http://placehold.it/32x32", - "name": "Myra Dejesus", - "company": "Capscreen", - "email": "myradejesus@capscreen.com", - "group": [ - "SV_XXX", - "EL_XXX" - ] - }, - { - "id": 25, - "picture": "http://placehold.it/32x32", - "name": "Brooks Santiago", - "company": "Elentrix", - "email": "brookssantiago@elentrix.com", - "group": [] - }, - { - "id": 26, - "picture": "http://placehold.it/32x32", - "name": "Simmons Franks", - "company": "Teraprene", - "email": "simmonsfranks@teraprene.com", - "group": [] - }, - { - "id": 27, - "picture": "http://placehold.it/32x32", - "name": "Ella Mercado", - "company": "Intradisk", - "email": "ellamercado@intradisk.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 28, - "picture": "http://placehold.it/32x32", - "name": "Heath Mooney", - "company": "Exoblue", - "email": "heathmooney@exoblue.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 29, - "picture": "http://placehold.it/32x32", - "name": "Therese Bowers", - "company": "Remold", - "email": "theresebowers@remold.com", - "group": [] - }, - { - "id": 30, - "picture": "http://placehold.it/32x32", - "name": "Helga Koch", - "company": "Zaya", - "email": "helgakoch@zaya.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 31, - "picture": "http://placehold.it/32x32", - "name": "Mcpherson Foster", - "company": "Exospeed", - "email": "mcphersonfoster@exospeed.com", - "group": [ - "SV_XXX", - "SV_YYY" - ] - }, - { - "id": 32, - "picture": "http://placehold.it/32x32", - "name": "Elsie Deleon", - "company": "Xinware", - "email": "elsiedeleon@xinware.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 33, - "picture": "http://placehold.it/32x32", - "name": "Reeves Riley", - "company": "Zolarity", - "email": "reevesriley@zolarity.com", - "group": [ - "SV_XXX", - "SV_YYY" - ] - }, - { - "id": 34, - "picture": "http://placehold.it/32x32", - "name": "Aurora Nash", - "company": "Pushcart", - "email": "auroranash@pushcart.com", - "group": [ - "SV_YYY", - "EL_YYY" - ] - }, - { - "id": 35, - "picture": "http://placehold.it/32x32", - "name": "Baker Levine", - "company": "Locazone", - "email": "bakerlevine@locazone.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 36, - "picture": "http://placehold.it/32x32", - "name": "Finch Tanner", - "company": "Poshome", - "email": "finchtanner@poshome.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 37, - "picture": "http://placehold.it/32x32", - "name": "Holcomb Winters", - "company": "Geoforma", - "email": "holcombwinters@geoforma.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 38, - "picture": "http://placehold.it/32x32", - "name": "Frost Fleming", - "company": "Pivitol", - "email": "frostfleming@pivitol.com", - "group": [] - }, - { - "id": 39, - "picture": "http://placehold.it/32x32", - "name": "Carmela Chandler", - "company": "Protodyne", - "email": "carmelachandler@protodyne.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 40, - "picture": "http://placehold.it/32x32", - "name": "Paula Rivers", - "company": "Entropix", - "email": "paularivers@entropix.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 41, - "picture": "http://placehold.it/32x32", - "name": "Lane Barrett", - "company": "Bunga", - "email": "lanebarrett@bunga.com", - "group": [ - "EL_XXX" - ] - }, - { - "id": 42, - "picture": "http://placehold.it/32x32", - "name": "Owen Vaughan", - "company": "Menbrain", - "email": "owenvaughan@menbrain.com", - "group": [] - }, - { - "id": 43, - "picture": "http://placehold.it/32x32", - "name": "Claudia Justice", - "company": "Imant", - "email": "claudiajustice@imant.com", - "group": [] - }, - { - "id": 44, - "picture": "http://placehold.it/32x32", - "name": "Hall Brock", - "company": "Affluex", - "email": "hallbrock@affluex.com", - "group": [ - "SV_YYY", - "EL_XXX" - ] - }, - { - "id": 45, - "picture": "http://placehold.it/32x32", - "name": "Joyner Whitfield", - "company": "Asimiline", - "email": "joynerwhitfield@asimiline.com", - "group": [ - "SV_XXX", - "EL_XXX" - ] - }, - { - "id": 46, - "picture": "http://placehold.it/32x32", - "name": "Maynard Bridges", - "company": "Liquicom", - "email": "maynardbridges@liquicom.com", - "group": [] - }, - { - "id": 47, - "picture": "http://placehold.it/32x32", - "name": "Ball Reed", - "company": "Premiant", - "email": "ballreed@premiant.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 48, - "picture": "http://placehold.it/32x32", - "name": "Karin Cole", - "company": "Zillactic", - "email": "karincole@zillactic.com", - "group": [ - "SV_XXX", - "EL_XXX" - ] - }, - { - "id": 49, - "picture": "http://placehold.it/32x32", - "name": "Mosley Whitaker", - "company": "Applidec", - "email": "mosleywhitaker@applidec.com", - "group": [ - "EL_YYY", - "SV_YYY" - ] - }, - { - "id": 50, - "picture": "http://placehold.it/32x32", - "name": "Naomi Brewer", - "company": "Equitax", - "email": "naomibrewer@equitax.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 51, - "picture": "http://placehold.it/32x32", - "name": "Lorna Kirk", - "company": "Valpreal", - "email": "lornakirk@valpreal.com", - "group": [ - "EL_XXX" - ] - }, - { - "id": 52, - "picture": "http://placehold.it/32x32", - "name": "Bowers Mccullough", - "company": "Newcube", - "email": "bowersmccullough@newcube.com", - "group": [ - "SV_XXX", - "EL_XXX" - ] - }, - { - "id": 53, - "picture": "http://placehold.it/32x32", - "name": "Delaney Preston", - "company": "Sensate", - "email": "delaneypreston@sensate.com", - "group": [] - }, - { - "id": 54, - "picture": "http://placehold.it/32x32", - "name": "Lara Patrick", - "company": "Quiltigen", - "email": "larapatrick@quiltigen.com", - "group": [] - }, - { - "id": 55, - "picture": "http://placehold.it/32x32", - "name": "Jimmie Hendrix", - "company": "Qaboos", - "email": "jimmiehendrix@qaboos.com", - "group": [] - }, - { - "id": 56, - "picture": "http://placehold.it/32x32", - "name": "Stafford Scott", - "company": "Chillium", - "email": "staffordscott@chillium.com", - "group": [] - }, - { - "id": 57, - "picture": "http://placehold.it/32x32", - "name": "Rosalie Skinner", - "company": "Kongle", - "email": "rosalieskinner@kongle.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 58, - "picture": "http://placehold.it/32x32", - "name": "Frye Hopper", - "company": "Digitalus", - "email": "fryehopper@digitalus.com", - "group": [ - "SV_YYY", - "Administrator" - ] - }, - { - "id": 59, - "picture": "http://placehold.it/32x32", - "name": "Lee Greer", - "company": "Adornica", - "email": "leegreer@adornica.com", - "group": [ - "SV_XXX", - "EL_XXX" - ] - }, - { - "id": 60, - "picture": "http://placehold.it/32x32", - "name": "Dena Oconnor", - "company": "Aquafire", - "email": "denaoconnor@aquafire.com", - "group": [ - "EL_YYY", - "EL_XXX" - ] - }, - { - "id": 61, - "picture": "http://placehold.it/32x32", - "name": "Robin Tillman", - "company": "Netility", - "email": "robintillman@netility.com", - "group": [] - }, - { - "id": 62, - "picture": "http://placehold.it/32x32", - "name": "Delores Burch", - "company": "Xth", - "email": "deloresburch@xth.com", - "group": [ - "EL_XXX" - ] - }, - { - "id": 63, - "picture": "http://placehold.it/32x32", - "name": "Kayla Carter", - "company": "Bedder", - "email": "kaylacarter@bedder.com", - "group": [ - "SV_YYY", - "Administrator" - ] - }, - { - "id": 64, - "picture": "http://placehold.it/32x32", - "name": "Janis Rutledge", - "company": "Geekular", - "email": "janisrutledge@geekular.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 65, - "picture": "http://placehold.it/32x32", - "name": "Carolyn Olson", - "company": "Marqet", - "email": "carolynolson@marqet.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 66, - "picture": "http://placehold.it/32x32", - "name": "Alberta Bennett", - "company": "Jetsilk", - "email": "albertabennett@jetsilk.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 67, - "picture": "http://placehold.it/32x32", - "name": "Benjamin Franco", - "company": "Earthmark", - "email": "benjaminfranco@earthmark.com", - "group": [] - }, - { - "id": 68, - "picture": "http://placehold.it/32x32", - "name": "Laurel Johnston", - "company": "Furnigeer", - "email": "laureljohnston@furnigeer.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 69, - "picture": "http://placehold.it/32x32", - "name": "Contreras Hayes", - "company": "Cinesanct", - "email": "contrerashayes@cinesanct.com", - "group": [ - "SV_YYY", - "EL_YYY" - ] - }, - { - "id": 70, - "picture": "http://placehold.it/32x32", - "name": "Mclean Dunlap", - "company": "Vitricomp", - "email": "mcleandunlap@vitricomp.com", - "group": [] - }, - { - "id": 71, - "picture": "http://placehold.it/32x32", - "name": "Bolton Herrera", - "company": "Toyletry", - "email": "boltonherrera@toyletry.com", - "group": [ - "EL_XXX", - "EL_YYY" - ] - }, - { - "id": 72, - "picture": "http://placehold.it/32x32", - "name": "Nikki Clay", - "company": "Ozean", - "email": "nikkiclay@ozean.com", - "group": [ - "EL_YYY", - "SV_YYY" - ] - }, - { - "id": 73, - "picture": "http://placehold.it/32x32", - "name": "Vega Wyatt", - "company": "Uni", - "email": "vegawyatt@uni.com", - "group": [] - }, - { - "id": 74, - "picture": "http://placehold.it/32x32", - "name": "Christine Sargent", - "company": "Zinca", - "email": "christinesargent@zinca.com", - "group": [ - "EL_XXX", - "SV_YYY" - ] - }, - { - "id": 75, - "picture": "http://placehold.it/32x32", - "name": "Blankenship Thompson", - "company": "Translink", - "email": "blankenshipthompson@translink.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 76, - "picture": "http://placehold.it/32x32", - "name": "Wade Brooks", - "company": "Cubicide", - "email": "wadebrooks@cubicide.com", - "group": [ - "SV_XXX", - "EL_XXX" - ] - }, - { - "id": 77, - "picture": "http://placehold.it/32x32", - "name": "Rosemary Gregory", - "company": "Corecom", - "email": "rosemarygregory@corecom.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 78, - "picture": "http://placehold.it/32x32", - "name": "Valencia Ashley", - "company": "Plasto", - "email": "valenciaashley@plasto.com", - "group": [ - "EL_XXX" - ] - }, - { - "id": 79, - "picture": "http://placehold.it/32x32", - "name": "Dorothea Woodward", - "company": "Telequiet", - "email": "dorotheawoodward@telequiet.com", - "group": [ - "EL_XXX", - "SV_YYY" - ] - }, - { - "id": 80, - "picture": "http://placehold.it/32x32", - "name": "Noelle Williams", - "company": "Flumbo", - "email": "noellewilliams@flumbo.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 81, - "picture": "http://placehold.it/32x32", - "name": "Letitia Cross", - "company": "Goko", - "email": "letitiacross@goko.com", - "group": [ - "EL_YYY", - "SV_YYY" - ] - }, - { - "id": 82, - "picture": "http://placehold.it/32x32", - "name": "Pamela Cleveland", - "company": "Skinserve", - "email": "pamelacleveland@skinserve.com", - "group": [ - "EL_YYY", - "EL_XXX" - ] - }, - { - "id": 83, - "picture": "http://placehold.it/32x32", - "name": "Lilian Barnes", - "company": "Zoid", - "email": "lilianbarnes@zoid.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 84, - "picture": "http://placehold.it/32x32", - "name": "Fuentes Sellers", - "company": "Andryx", - "email": "fuentessellers@andryx.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 85, - "picture": "http://placehold.it/32x32", - "name": "Rivers Sanders", - "company": "Slofast", - "email": "riverssanders@slofast.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 86, - "picture": "http://placehold.it/32x32", - "name": "Cameron Wiggins", - "company": "Codax", - "email": "cameronwiggins@codax.com", - "group": [] - }, - { - "id": 87, - "picture": "http://placehold.it/32x32", - "name": "Allen Lynn", - "company": "Xoggle", - "email": "allenlynn@xoggle.com", - "group": [] - }, - { - "id": 88, - "picture": "http://placehold.it/32x32", - "name": "Sabrina Rollins", - "company": "Apextri", - "email": "sabrinarollins@apextri.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 89, - "picture": "http://placehold.it/32x32", - "name": "Guadalupe Hayden", - "company": "Neptide", - "email": "guadalupehayden@neptide.com", - "group": [ - "SV_YYY", - "EL_XXX" - ] - }, - { - "id": 90, - "picture": "http://placehold.it/32x32", - "name": "Jody Mcleod", - "company": "Organica", - "email": "jodymcleod@organica.com", - "group": [ - "EL_XXX", - "SV_YYY" - ] - }, - { - "id": 91, - "picture": "http://placehold.it/32x32", - "name": "Wiley Doyle", - "company": "Sarasonic", - "email": "wileydoyle@sarasonic.com", - "group": [ - "EL_XXX", - "Administrator" - ] - }, - { - "id": 92, - "picture": "http://placehold.it/32x32", - "name": "Foster Hensley", - "company": "Rooforia", - "email": "fosterhensley@rooforia.com", - "group": [] - }, - { - "id": 93, - "picture": "http://placehold.it/32x32", - "name": "Hill Chen", - "company": "Viagreat", - "email": "hillchen@viagreat.com", - "group": [ - "SV_YYY", - "EL_XXX" - ] - }, - { - "id": 94, - "picture": "http://placehold.it/32x32", - "name": "Sharlene Hanson", - "company": "Comcur", - "email": "sharlenehanson@comcur.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 95, - "picture": "http://placehold.it/32x32", - "name": "Winifred Washington", - "company": "Maineland", - "email": "winifredwashington@maineland.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 96, - "picture": "http://placehold.it/32x32", - "name": "Howe Graham", - "company": "Fortean", - "email": "howegraham@fortean.com", - "group": [] - }, - { - "id": 97, - "picture": "http://placehold.it/32x32", - "name": "Saunders Higgins", - "company": "Powernet", - "email": "saundershiggins@powernet.com", - "group": [ - "EL_XXX" - ] - }, - { - "id": 98, - "picture": "http://placehold.it/32x32", - "name": "Christy Tran", - "company": "Combot", - "email": "christytran@combot.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 99, - "picture": "http://placehold.it/32x32", - "name": "Clare Case", - "company": "Escenta", - "email": "clarecase@escenta.com", - "group": [ - "SV_XXX" - ] - } - ] diff --git a/ldapadmin/src/main/webapp/privateui/data/users/all.json~ b/ldapadmin/src/main/webapp/privateui/data/users/all.json~ deleted file mode 100644 index d1fafc1515..0000000000 --- a/ldapadmin/src/main/webapp/privateui/data/users/all.json~ +++ /dev/null @@ -1,1089 +0,0 @@ -[ - { - "id": 0, - "picture": "http://placehold.it/32x32", - "name": "Castillo Foreman", - "company": "Aeora", - "email": "castilloforeman@aeora.com", - "group": [ - "EL_XXX", - "SV_YYY", - "Administrator", - "EL_YYY" - ] - }, - { - "id": 1, - "picture": "http://placehold.it/32x32", - "name": "Price Dyer", - "company": "Uncorp", - "email": "pricedyer@uncorp.com", - "group": [ - "EL_XXX" - ] - }, - { - "id": 2, - "picture": "http://placehold.it/32x32", - "name": "Ortega Garrison", - "company": "Polarax", - "email": "ortegagarrison@polarax.com", - "group": [ - "EL_YYY", - "Administrator" - ] - }, - { - "id": 3, - "picture": "http://placehold.it/32x32", - "name": "Sue Lewis", - "company": "Zeam", - "email": "suelewis@zeam.com", - "group": [ - "EL_XXX", - "EL_YYY" - ] - }, - { - "id": 4, - "picture": "http://placehold.it/32x32", - "name": "Leanne Simmons", - "company": "Zinca", - "email": "leannesimmons@zinca.com", - "group": [] - }, - { - "id": 5, - "picture": "http://placehold.it/32x32", - "name": "Joann Mccullough", - "company": "Eventix", - "email": "joannmccullough@eventix.com", - "group": [ - "SV_YYY", - "EL_XXX", - "SV_XXX" - ] - }, - { - "id": 6, - "picture": "http://placehold.it/32x32", - "name": "Nancy Abbott", - "company": "Incubus", - "email": "nancyabbott@incubus.com", - "group": [ - "SV_XXX", - "SV_YYY", - "EL_XXX", - "EL_YYY" - ] - }, - { - "id": 7, - "picture": "http://placehold.it/32x32", - "name": "Curry Walker", - "company": "Ramjob", - "email": "currywalker@ramjob.com", - "group": [ - "SV_XXX", - "SV_YYY", - "EL_YYY" - ] - }, - { - "id": 8, - "picture": "http://placehold.it/32x32", - "name": "Sampson Brooks", - "company": "Quilk", - "email": "sampsonbrooks@quilk.com", - "group": [] - }, - { - "id": 9, - "picture": "http://placehold.it/32x32", - "name": "Lucy Mosley", - "company": "Koffee", - "email": "lucymosley@koffee.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 10, - "picture": "http://placehold.it/32x32", - "name": "Valentine Wilkerson", - "company": "Ronelon", - "email": "valentinewilkerson@ronelon.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 11, - "picture": "http://placehold.it/32x32", - "name": "Saunders Gardner", - "company": "Entogrok", - "email": "saundersgardner@entogrok.com", - "group": [ - "SV_XXX", - "EL_XXX" - ] - }, - { - "id": 12, - "picture": "http://placehold.it/32x32", - "name": "Byrd Carney", - "company": "Letpro", - "email": "byrdcarney@letpro.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 13, - "picture": "http://placehold.it/32x32", - "name": "Erica Oconnor", - "company": "Eclipsent", - "email": "ericaoconnor@eclipsent.com", - "group": [ - "EL_XXX", - "EL_YYY", - "SV_XXX", - "SV_YYY" - ] - }, - { - "id": 14, - "picture": "http://placehold.it/32x32", - "name": "Stokes Orr", - "company": "Canopoly", - "email": "stokesorr@canopoly.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 15, - "picture": "http://placehold.it/32x32", - "name": "Marisa Campbell", - "company": "Sarasonic", - "email": "marisacampbell@sarasonic.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 16, - "picture": "http://placehold.it/32x32", - "name": "Malinda Mcdonald", - "company": "Zytrac", - "email": "malindamcdonald@zytrac.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 17, - "picture": "http://placehold.it/32x32", - "name": "Caroline Cline", - "company": "Veraq", - "email": "carolinecline@veraq.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 18, - "picture": "http://placehold.it/32x32", - "name": "Rose Massey", - "company": "Quantasis", - "email": "rosemassey@quantasis.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 19, - "picture": "http://placehold.it/32x32", - "name": "Hatfield Medina", - "company": "Toyletry", - "email": "hatfieldmedina@toyletry.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 20, - "picture": "http://placehold.it/32x32", - "name": "Esperanza Lynch", - "company": "Austex", - "email": "esperanzalynch@austex.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 21, - "picture": "http://placehold.it/32x32", - "name": "Salazar Richards", - "company": "Songbird", - "email": "salazarrichards@songbird.com", - "group": [ - "SV_YYY", - "Administrator" - ] - }, - { - "id": 22, - "picture": "http://placehold.it/32x32", - "name": "Annette Mccarty", - "company": "Visalia", - "email": "annettemccarty@visalia.com", - "group": [ - "EL_YYY", - "Administrator", - "EL_XXX", - "SV_XXX" - ] - }, - { - "id": 23, - "picture": "http://placehold.it/32x32", - "name": "Russell Thornton", - "company": "Namegen", - "email": "russellthornton@namegen.com", - "group": [ - "SV_XXX", - "SV_YYY", - "EL_YYY" - ] - }, - { - "id": 24, - "picture": "http://placehold.it/32x32", - "name": "Winters Robertson", - "company": "Hotcakes", - "email": "wintersrobertson@hotcakes.com", - "group": [ - "EL_YYY", - "SV_YYY", - "Administrator", - "SV_XXX" - ] - }, - { - "id": 25, - "picture": "http://placehold.it/32x32", - "name": "Hudson Sanchez", - "company": "Medifax", - "email": "hudsonsanchez@medifax.com", - "group": [ - "EL_XXX", - "Administrator" - ] - }, - { - "id": 26, - "picture": "http://placehold.it/32x32", - "name": "Hester Diaz", - "company": "Evidends", - "email": "hesterdiaz@evidends.com", - "group": [] - }, - { - "id": 27, - "picture": "http://placehold.it/32x32", - "name": "Parker Weeks", - "company": "Envire", - "email": "parkerweeks@envire.com", - "group": [] - }, - { - "id": 28, - "picture": "http://placehold.it/32x32", - "name": "Elsie Spence", - "company": "Providco", - "email": "elsiespence@providco.com", - "group": [] - }, - { - "id": 29, - "picture": "http://placehold.it/32x32", - "name": "Emerson Mendoza", - "company": "Limozen", - "email": "emersonmendoza@limozen.com", - "group": [] - }, - { - "id": 30, - "picture": "http://placehold.it/32x32", - "name": "Gordon Levine", - "company": "Uni", - "email": "gordonlevine@uni.com", - "group": [ - "EL_YYY", - "EL_XXX", - "SV_XXX" - ] - }, - { - "id": 31, - "picture": "http://placehold.it/32x32", - "name": "Ericka Day", - "company": "Slax", - "email": "erickaday@slax.com", - "group": [ - "EL_XXX", - "Administrator", - "EL_YYY", - "SV_YYY" - ] - }, - { - "id": 32, - "picture": "http://placehold.it/32x32", - "name": "Riggs Head", - "company": "Isostream", - "email": "riggshead@isostream.com", - "group": [ - "SV_XXX", - "EL_XXX", - "EL_YYY", - "SV_YYY" - ] - }, - { - "id": 33, - "picture": "http://placehold.it/32x32", - "name": "Corrine Moreno", - "company": "Decratex", - "email": "corrinemoreno@decratex.com", - "group": [] - }, - { - "id": 34, - "picture": "http://placehold.it/32x32", - "name": "Lorraine Butler", - "company": "Ebidco", - "email": "lorrainebutler@ebidco.com", - "group": [ - "EL_YYY", - "EL_XXX" - ] - }, - { - "id": 35, - "picture": "http://placehold.it/32x32", - "name": "Simpson Sellers", - "company": "Orbaxter", - "email": "simpsonsellers@orbaxter.com", - "group": [ - "SV_XXX", - "EL_XXX", - "Administrator" - ] - }, - { - "id": 36, - "picture": "http://placehold.it/32x32", - "name": "Amber Finch", - "company": "Naxdis", - "email": "amberfinch@naxdis.com", - "group": [ - "EL_XXX" - ] - }, - { - "id": 37, - "picture": "http://placehold.it/32x32", - "name": "Bass Middleton", - "company": "Interfind", - "email": "bassmiddleton@interfind.com", - "group": [ - "EL_YYY", - "EL_XXX", - "SV_XXX" - ] - }, - { - "id": 38, - "picture": "http://placehold.it/32x32", - "name": "Weeks Spears", - "company": "Kraggle", - "email": "weeksspears@kraggle.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 39, - "picture": "http://placehold.it/32x32", - "name": "Gilbert Pennington", - "company": "Ginkogene", - "email": "gilbertpennington@ginkogene.com", - "group": [] - }, - { - "id": 40, - "picture": "http://placehold.it/32x32", - "name": "Howard Rios", - "company": "Maineland", - "email": "howardrios@maineland.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 41, - "picture": "http://placehold.it/32x32", - "name": "Earnestine Arnold", - "company": "Infotrips", - "email": "earnestinearnold@infotrips.com", - "group": [] - }, - { - "id": 42, - "picture": "http://placehold.it/32x32", - "name": "Lottie Houston", - "company": "Colaire", - "email": "lottiehouston@colaire.com", - "group": [ - "SV_XXX", - "EL_XXX" - ] - }, - { - "id": 43, - "picture": "http://placehold.it/32x32", - "name": "Fran Barnes", - "company": "Zomboid", - "email": "franbarnes@zomboid.com", - "group": [ - "SV_XXX", - "SV_YYY" - ] - }, - { - "id": 44, - "picture": "http://placehold.it/32x32", - "name": "Keri Oliver", - "company": "Techade", - "email": "kerioliver@techade.com", - "group": [ - "SV_XXX", - "SV_YYY", - "EL_XXX", - "EL_YYY" - ] - }, - { - "id": 45, - "picture": "http://placehold.it/32x32", - "name": "Kendra Becker", - "company": "Geekfarm", - "email": "kendrabecker@geekfarm.com", - "group": [ - "SV_YYY", - "EL_XXX", - "EL_YYY", - "Administrator" - ] - }, - { - "id": 46, - "picture": "http://placehold.it/32x32", - "name": "Samantha Petersen", - "company": "Voipa", - "email": "samanthapetersen@voipa.com", - "group": [ - "EL_XXX", - "EL_YYY", - "Administrator", - "SV_XXX" - ] - }, - { - "id": 47, - "picture": "http://placehold.it/32x32", - "name": "Ellen Sanford", - "company": "Amtas", - "email": "ellensanford@amtas.com", - "group": [ - "SV_XXX", - "EL_YYY" - ] - }, - { - "id": 48, - "picture": "http://placehold.it/32x32", - "name": "Stanton Pate", - "company": "Prosely", - "email": "stantonpate@prosely.com", - "group": [ - "SV_YYY", - "Administrator", - "EL_XXX" - ] - }, - { - "id": 49, - "picture": "http://placehold.it/32x32", - "name": "Maritza Chase", - "company": "Gynko", - "email": "maritzachase@gynko.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 50, - "picture": "http://placehold.it/32x32", - "name": "Camille Bauer", - "company": "Oatfarm", - "email": "camillebauer@oatfarm.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 51, - "picture": "http://placehold.it/32x32", - "name": "Juana Sheppard", - "company": "Applica", - "email": "juanasheppard@applica.com", - "group": [ - "SV_XXX", - "EL_YYY", - "EL_XXX", - "Administrator" - ] - }, - { - "id": 52, - "picture": "http://placehold.it/32x32", - "name": "Christi Burch", - "company": "Senmao", - "email": "christiburch@senmao.com", - "group": [ - "EL_YYY", - "SV_YYY", - "EL_XXX" - ] - }, - { - "id": 53, - "picture": "http://placehold.it/32x32", - "name": "Whitfield Dillon", - "company": "Blanet", - "email": "whitfielddillon@blanet.com", - "group": [] - }, - { - "id": 54, - "picture": "http://placehold.it/32x32", - "name": "Corine Craft", - "company": "Equicom", - "email": "corinecraft@equicom.com", - "group": [ - "SV_YYY", - "EL_XXX", - "EL_YYY", - "Administrator" - ] - }, - { - "id": 55, - "picture": "http://placehold.it/32x32", - "name": "Garrison Whitehead", - "company": "Supportal", - "email": "garrisonwhitehead@supportal.com", - "group": [] - }, - { - "id": 56, - "picture": "http://placehold.it/32x32", - "name": "Becker Rutledge", - "company": "Barkarama", - "email": "beckerrutledge@barkarama.com", - "group": [ - "SV_YYY", - "Administrator", - "EL_YYY" - ] - }, - { - "id": 57, - "picture": "http://placehold.it/32x32", - "name": "Carrillo Mayer", - "company": "Twiggery", - "email": "carrillomayer@twiggery.com", - "group": [] - }, - { - "id": 58, - "picture": "http://placehold.it/32x32", - "name": "Marsh Monroe", - "company": "Exozent", - "email": "marshmonroe@exozent.com", - "group": [] - }, - { - "id": 59, - "picture": "http://placehold.it/32x32", - "name": "Golden Kirk", - "company": "Edecine", - "email": "goldenkirk@edecine.com", - "group": [ - "EL_YYY", - "SV_YYY", - "EL_XXX", - "Administrator" - ] - }, - { - "id": 60, - "picture": "http://placehold.it/32x32", - "name": "Cortez Mathis", - "company": "Tourmania", - "email": "cortezmathis@tourmania.com", - "group": [ - "SV_YYY", - "EL_YYY", - "EL_XXX" - ] - }, - { - "id": 61, - "picture": "http://placehold.it/32x32", - "name": "Nelda Bass", - "company": "Powernet", - "email": "neldabass@powernet.com", - "group": [ - "EL_XXX", - "SV_YYY" - ] - }, - { - "id": 62, - "picture": "http://placehold.it/32x32", - "name": "Karin Marsh", - "company": "Hinway", - "email": "karinmarsh@hinway.com", - "group": [ - "EL_XXX" - ] - }, - { - "id": 63, - "picture": "http://placehold.it/32x32", - "name": "Burnett Crosby", - "company": "Pyramax", - "email": "burnettcrosby@pyramax.com", - "group": [ - "EL_YYY", - "SV_YYY", - "Administrator", - "SV_XXX" - ] - }, - { - "id": 64, - "picture": "http://placehold.it/32x32", - "name": "Ball Browning", - "company": "Kidgrease", - "email": "ballbrowning@kidgrease.com", - "group": [] - }, - { - "id": 65, - "picture": "http://placehold.it/32x32", - "name": "Sheena Brady", - "company": "Mondicil", - "email": "sheenabrady@mondicil.com", - "group": [ - "EL_YYY", - "EL_XXX", - "SV_XXX" - ] - }, - { - "id": 66, - "picture": "http://placehold.it/32x32", - "name": "Louella Cannon", - "company": "Ronbert", - "email": "louellacannon@ronbert.com", - "group": [ - "EL_YYY", - "Administrator", - "SV_XXX" - ] - }, - { - "id": 67, - "picture": "http://placehold.it/32x32", - "name": "Leann Mason", - "company": "Extro", - "email": "leannmason@extro.com", - "group": [ - "SV_YYY", - "EL_YYY", - "EL_XXX" - ] - }, - { - "id": 68, - "picture": "http://placehold.it/32x32", - "name": "Tammy Kidd", - "company": "Medcom", - "email": "tammykidd@medcom.com", - "group": [ - "SV_XXX", - "SV_YYY", - "EL_XXX", - "EL_YYY" - ] - }, - { - "id": 69, - "picture": "http://placehold.it/32x32", - "name": "Simmons Dixon", - "company": "Extragene", - "email": "simmonsdixon@extragene.com", - "group": [] - }, - { - "id": 70, - "picture": "http://placehold.it/32x32", - "name": "Dolores Burris", - "company": "Exposa", - "email": "doloresburris@exposa.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 71, - "picture": "http://placehold.it/32x32", - "name": "Alta Brock", - "company": "Trollery", - "email": "altabrock@trollery.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 72, - "picture": "http://placehold.it/32x32", - "name": "Goodman Griffin", - "company": "Bolax", - "email": "goodmangriffin@bolax.com", - "group": [ - "SV_YYY", - "EL_YYY", - "SV_XXX" - ] - }, - { - "id": 73, - "picture": "http://placehold.it/32x32", - "name": "Vang Deleon", - "company": "Volax", - "email": "vangdeleon@volax.com", - "group": [] - }, - { - "id": 74, - "picture": "http://placehold.it/32x32", - "name": "Latasha Hays", - "company": "Netility", - "email": "latashahays@netility.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 75, - "picture": "http://placehold.it/32x32", - "name": "Roth King", - "company": "Cedward", - "email": "rothking@cedward.com", - "group": [ - "SV_XXX", - "EL_XXX" - ] - }, - { - "id": 76, - "picture": "http://placehold.it/32x32", - "name": "Christie Martin", - "company": "Zolarity", - "email": "christiemartin@zolarity.com", - "group": [ - "SV_XXX" - ] - }, - { - "id": 77, - "picture": "http://placehold.it/32x32", - "name": "Brenda Mcintosh", - "company": "Quadeebo", - "email": "brendamcintosh@quadeebo.com", - "group": [ - "SV_XXX", - "SV_YYY" - ] - }, - { - "id": 78, - "picture": "http://placehold.it/32x32", - "name": "Winifred Robinson", - "company": "Cowtown", - "email": "winifredrobinson@cowtown.com", - "group": [ - "EL_YYY", - "EL_XXX", - "Administrator", - "SV_YYY" - ] - }, - { - "id": 79, - "picture": "http://placehold.it/32x32", - "name": "Obrien Stark", - "company": "Isologix", - "email": "obrienstark@isologix.com", - "group": [ - "SV_YYY", - "EL_YYY" - ] - }, - { - "id": 80, - "picture": "http://placehold.it/32x32", - "name": "Everett Hardin", - "company": "Accel", - "email": "everetthardin@accel.com", - "group": [ - "EL_YYY", - "EL_XXX", - "SV_XXX" - ] - }, - { - "id": 81, - "picture": "http://placehold.it/32x32", - "name": "Hillary Hutchinson", - "company": "Xylar", - "email": "hillaryhutchinson@xylar.com", - "group": [ - "SV_YYY", - "EL_XXX", - "EL_YYY", - "Administrator" - ] - }, - { - "id": 82, - "picture": "http://placehold.it/32x32", - "name": "Joan Doyle", - "company": "Darwinium", - "email": "joandoyle@darwinium.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 83, - "picture": "http://placehold.it/32x32", - "name": "Tricia Irwin", - "company": "Isbol", - "email": "triciairwin@isbol.com", - "group": [ - "SV_YYY", - "EL_YYY" - ] - }, - { - "id": 84, - "picture": "http://placehold.it/32x32", - "name": "Sherrie Turner", - "company": "Calcula", - "email": "sherrieturner@calcula.com", - "group": [ - "SV_XXX", - "EL_YYY", - "Administrator" - ] - }, - { - "id": 85, - "picture": "http://placehold.it/32x32", - "name": "Kelley Emerson", - "company": "Intrawear", - "email": "kelleyemerson@intrawear.com", - "group": [ - "EL_YYY", - "Administrator", - "SV_XXX", - "SV_YYY" - ] - }, - { - "id": 86, - "picture": "http://placehold.it/32x32", - "name": "Lynch Long", - "company": "Quordate", - "email": "lynchlong@quordate.com", - "group": [ - "EL_YYY", - "EL_XXX", - "SV_XXX" - ] - }, - { - "id": 87, - "picture": "http://placehold.it/32x32", - "name": "Chris Weiss", - "company": "Zilladyne", - "email": "chrisweiss@zilladyne.com", - "group": [ - "EL_XXX", - "SV_YYY", - "Administrator" - ] - }, - { - "id": 88, - "picture": "http://placehold.it/32x32", - "name": "Brennan Talley", - "company": "Silodyne", - "email": "brennantalley@silodyne.com", - "group": [ - "EL_XXX", - "SV_YYY", - "Administrator", - "EL_YYY" - ] - }, - { - "id": 89, - "picture": "http://placehold.it/32x32", - "name": "Bowers Tyler", - "company": "Mantrix", - "email": "bowerstyler@mantrix.com", - "group": [ - "SV_XXX", - "EL_XXX", - "EL_YYY" - ] - }, - { - "id": 90, - "picture": "http://placehold.it/32x32", - "name": "Nixon Boyd", - "company": "Glasstep", - "email": "nixonboyd@glasstep.com", - "group": [ - "SV_XXX", - "EL_XXX", - "EL_YYY" - ] - }, - { - "id": 91, - "picture": "http://placehold.it/32x32", - "name": "Wise Haynes", - "company": "Netbook", - "email": "wisehaynes@netbook.com", - "group": [ - "SV_YYY" - ] - }, - { - "id": 92, - "picture": "http://placehold.it/32x32", - "name": "Williamson Dickson", - "company": "Zizzle", - "email": "williamsondickson@zizzle.com", - "group": [ - "EL_YYY", - "SV_YYY", - "Administrator" - ] - }, - { - "id": 93, - "picture": "http://placehold.it/32x32", - "name": "Rosie Baker", - "company": "Plasmos", - "email": "rosiebaker@plasmos.com", - "group": [ - "EL_YYY", - "Administrator" - ] - }, - { - "id": 94, - "picture": "http://placehold.it/32x32", - "name": "Lee Brewer", - "company": "Stelaecor", - "email": "leebrewer@stelaecor.com", - "group": [ - "EL_YYY", - "EL_XXX" - ] - }, - { - "id": 95, - "picture": "http://placehold.it/32x32", - "name": "Henderson Malone", - "company": "Zillactic", - "email": "hendersonmalone@zillactic.com", - "group": [ - "SV_XXX", - "EL_XXX", - "Administrator", - "EL_YYY" - ] - }, - { - "id": 96, - "picture": "http://placehold.it/32x32", - "name": "Reva Horne", - "company": "Insuresys", - "email": "revahorne@insuresys.com", - "group": [] - }, - { - "id": 97, - "picture": "http://placehold.it/32x32", - "name": "Gail Larsen", - "company": "Krag", - "email": "gaillarsen@krag.com", - "group": [ - "EL_YYY" - ] - }, - { - "id": 98, - "picture": "http://placehold.it/32x32", - "name": "Cox Waller", - "company": "Ovation", - "email": "coxwaller@ovation.com", - "group": [ - "EL_XXX", - "Administrator" - ] - }, - { - "id": 99, - "picture": "http://placehold.it/32x32", - "name": "Matilda Peck", - "company": "Sureplex", - "email": "matildapeck@sureplex.com", - "group": [ - "SV_XXX", - "EL_YYY", - "EL_XXX" - ] - } - ] diff --git a/ldapadmin/src/main/webapp/privateui/data/users/toto.json b/ldapadmin/src/main/webapp/privateui/data/users/toto.json deleted file mode 100644 index b8e7bf59d2..0000000000 --- a/ldapadmin/src/main/webapp/privateui/data/users/toto.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "toti", - "email": "toti@domaine.com", - "description": "Le meilleur de tous" -} diff --git a/ldapadmin/src/main/webapp/privateui/img/.gitkeep b/ldapadmin/src/main/webapp/privateui/img/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ldapadmin/src/main/webapp/privateui/js/app.js b/ldapadmin/src/main/webapp/privateui/js/app.js deleted file mode 100644 index 2b176f98d2..0000000000 --- a/ldapadmin/src/main/webapp/privateui/js/app.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -/* App Module */ - -angular.module('ldapadmin', ['ldapadmin.filters', 'ldapadmin.controllers', 'ldapadmin.directives', 'restangular', 'angular-flash.flash-alert-directive']). - config(['$routeProvider', function($routeProvider) { - $routeProvider. - when('/users', {templateUrl: 'partials/users-list.html', controller: 'UsersListCtrl'}). - when('/users/new', {templateUrl: 'partials/user-edit.html', controller: 'UserCreateCtrl'}). - when('/users/:userId', {templateUrl: 'partials/user-edit.html', controller: 'UserEditCtrl'}). - when('/groups/new', {templateUrl: 'partials/group-edit.html', controller: 'GroupCreateCtrl'}). - when('/groups/:group', {templateUrl: 'partials/users-list.html', controller: 'UsersListCtrl'}). - when('/groups/:group/edit', {templateUrl: 'partials/group-edit.html', controller: 'GroupEditCtrl'}). - otherwise({redirectTo: '/users'}); - } - ]) - .config(function(RestangularProvider) { - RestangularProvider.setBaseUrl(GEOR_config.publicContextPath + "/private"); - }) - .filter('encodeURIComponent', function() { - return window.encodeURIComponent; - }); diff --git a/ldapadmin/src/main/webapp/privateui/js/controllers.js b/ldapadmin/src/main/webapp/privateui/js/controllers.js deleted file mode 100644 index fd07deb20d..0000000000 --- a/ldapadmin/src/main/webapp/privateui/js/controllers.js +++ /dev/null @@ -1,507 +0,0 @@ -'use strict'; - -/* Controllers */ -angular.module('ldapadmin.controllers', []) - .controller('GroupsCtrl', function($scope, $rootScope, Restangular) { - $rootScope.groups = []; - Restangular.all('groups').getList().then(function(groups) { - $rootScope.groups = groups; - - var tree = []; - var prefix; - angular.forEach($rootScope.groups, function(group, key) { - addNode(tree, group); - }); - $rootScope.groups_tree = tree; - }, function errorCallback() { - flash.error = 'Oops error from server :('; - }); - }) - /** - * Group Edit - */ - .controller('GroupEditCtrl', function($scope, $routeParams, Restangular, flash) { - var group = Restangular.one('groups', $routeParams.group); - group.get().then(function(remote) { - $scope.group = Restangular.copy(remote); - - // manually add an id field so that we can use Restangular without - // changing the mapping to id field globally - $scope.group.id = $scope.group.cn; - - $scope.save = function() { - $scope.group.put().then(function() { - flash.success = 'Group correctly updated'; - var index = findByAttr($scope.groups, 'cn', $routeParams.group); - - if (index !== false) { - $scope.groups[index].cn = $scope.group.cn; - remote = angular.copy($scope.group); - window.location = '#/groups/' + $scope.group.cn + '/edit'; - } - }); - }; - $scope.isClean = function() { - return angular.equals(remote, $scope.group); - }; - $scope.cancel = function() { - window.location = '#/groups/' + $scope.group.cn; - }; - }); - }) - /** - * Group Create - */ - .controller('GroupCreateCtrl', function($scope, Restangular, flash) { - $scope.save = function() { - Restangular.all('groups').post( - $scope.group - ).then(function(group) { - $scope.groups.push(group); - - // update groups tree - addNode($scope.groups_tree, group); - - window.location = "#/users"; - flash.success = 'Group correctly added'; - }, function errorCallback() { - flash.error = 'Error creating the group :('; - }); - }; - }) - .controller('UsersCtrl', function UsersCtrl($scope, Restangular) { - var baseUsers = Restangular.all('users'); - baseUsers.getList().then(function(users) { - $scope.users = users; - }, function errorCallback() { - flash.error = 'Oops error from server :('; - }); - }) - - /** - * Users List - */ - .controller('UsersListCtrl', function($scope, $rootScope, $routeParams, $filter, Restangular, flash) { - var group; - - function selectGroup() { - //$scope.users is inherited from UsersCtrl's scope - if ($routeParams.group == 'none') { - var groups = [ - $scope.groups[findByAttr($scope.groups, 'cn', 'ADMINISTRATOR')], - $scope.groups[findByAttr($scope.groups, 'cn', 'SV_ADMIN')], - $scope.groups[findByAttr($scope.groups, 'cn', 'SV_REVIEWER')], - $scope.groups[findByAttr($scope.groups, 'cn', 'SV_EDITOR')], - $scope.groups[findByAttr($scope.groups, 'cn', 'SV_USER')] - ]; - $scope.groupFilter = function(item) { - return groups[0].users.indexOf(item.uid) == -1 && - groups[1].users.indexOf(item.uid) == -1 && - groups[2].users.indexOf(item.uid) == -1 && - groups[3].users.indexOf(item.uid) == -1 && - groups[4].users.indexOf(item.uid) == -1; - }; - $rootScope.selectedGroup = "none"; - $scope.allSelected = false; - } else { - var index = findByAttr($scope.groups, 'cn', $routeParams.group); - group = $scope.groups[index]; - $scope.groupFilter = function(item) { - if (group) { - return group.users && group.users.indexOf(item.uid) != -1; - } else { - return true; - } - }; - $rootScope.selectedGroup = group; - $scope.allSelected = false; - } - } - - // wait for groups to be loaded from service, prevents race condition - $scope.$watch('groups', function() { - selectGroup(); - }); - - $scope.selectedUsers = function() { - return _.filter($scope.users, function(user) { - return user.selected === true && - (!group || group && group.users && group.users.indexOf(user.uid) != -1); - }); - }; - - function filteredUsers() { - return _.filter($scope.users, function(user) { - return !group || group.users && group.users.indexOf(user.uid) != -1; - }); - } - - $scope.$watch('users', function() { - var filtered = filteredUsers(), - selected = $scope.selectedUsers(); - - $scope.allSelected = filtered && selected && - selected.length == filtered.length && - filtered.length > 0; - }, true); - - $scope.selectAll = function() { - angular.forEach(filteredUsers(), function(user) { - user.selected = $scope.allSelected; - }); - }; - - function hasUsers(group) { - var total = $scope.selectedUsers().length; - var uids = _.pluck($scope.selectedUsers(), 'uid'); - var inGroup = _.difference(uids, group.users); - if (inGroup.length === total) { - return false; - } - return inGroup.length === 0 ? 'all' : 'some'; - } - $scope.selectGroup = function(group) { - if (!group.hasUsers || group.hasUsers == 'some') { - group.hasUsers = 'all'; - } else if (group.hasUsers == 'all') { - group.hasUsers = false; - } - // check whether the list of groups changed - $scope.groupsChanged = !angular.equals($scope.original_groups, $scope.user_groups); - }; - - // we wan't to initialize groups when the groups button is clicked - $scope.initGroups = function() { - // A copy of list of groups (w/ information on whether the user is part - // of this group or not - $scope.user_groups = angular.copy($scope.groups); - - var tree = []; - var prefix; - angular.forEach($scope.user_groups, function(group, key) { - addNode(tree, group); - }); - $scope.user_groups_tree = tree; - - angular.forEach($scope.user_groups, function(group, key) { - group.hasUsers = hasUsers(group); - }); - - $scope.original_groups = angular.copy($scope.user_groups); - - $scope.groupsChanged = false; - }; - - // called when user submits modifications on groups list for a user - $scope.apply = function() { - postGroups($scope, $scope.selectedUsers(), Restangular, flash); - }; - $scope.deleteGroup = function(group) { - - if(group == GEOR_config.virtualTemporaryGroupName) { - alert("This group cannot be deleted because it's a virtual group !"); - return; - } - - if (confirm('Do you really want to remove group "' + group + '"?')) { - Restangular.one('groups', group).remove().then(function() { - var index = findByAttr($scope.groups, 'cn', $routeParams.group); - - if (index !== false) { - $scope.groups = $scope.groups.splice(index, 1); - removeNode($scope.groups_tree, group); - } - window.location = '#/users'; - flash.success = 'Group correctly removed'; - }, function errorCallback() { - flash.error = 'Error removing the group'; - }); - } - }; - - $scope.exportAsCsv = function() { - var uids = _.pluck($scope.selectedUsers(), 'uid'); - var url = GEOR_config.publicContextPath + "/private/users.csv"; - - var form = $('
').attr("action", url).attr("method", 'POST').attr("id", '#exportAsCsvForm'); - var input = $('').attr("name", 'users'); - - form.append(input).appendTo('body'); - form.find("input[name=users]").val(JSON.stringify(uids)); - form.submit(); - form.remove(); - } - - $scope.exportAsVcard = function() { - var uids = _.pluck($scope.selectedUsers(), 'uid'); - var url = GEOR_config.publicContextPath + "/private/users.vcf"; - - var form = $('
').attr("action", url).attr("method", 'POST').attr("id", '#exportAsVcfForm'); - var input = $('').attr("name", 'users'); - - form.append(input).appendTo('body'); - form.find("input[name=users]").val(JSON.stringify(uids)); - form.submit(); - form.remove(); - } - }) - - /** - * User Edit - */ - .controller('UserEditCtrl', function($scope, $routeParams, Restangular, flash) { - var user = Restangular.one('users', $routeParams.userId); - user.get().then(function(remote) { - - // manually add an id field so that we can use Restangular without - // changing the mapping to id field globally - remote.id = remote.uid; - - $scope.user = Restangular.copy(remote); - - $scope.groupsChanged = false; - $scope.publicContextPath = GEOR_config.publicContextPath; - - $scope.save = function() { - $scope.user.put().then(function() { - flash.success = 'User correctly updated'; - var prevUserId = $routeParams.userId; - var newUserId = $scope.user.uid; - var index = findByAttr($scope.users, 'uid', prevUserId); - - if (index !== false) { - $scope.users[index] = angular.copy($scope.user); - remote = angular.copy($scope.user); - - // uid modified - if (newUserId != prevUserId) { - window.location = '#/users/' + newUserId; - - // Update the groups the user belongs to - var i, - len = $scope.groups.length; - for (i=0; i < len; i++) { - var index2 = _.indexOf($scope.groups[i].users, prevUserId); - if (index2 != -1) { - $scope.groups[i].users[index2] = newUserId; - } - } - } - } - }, function(args) { - flash.error = 'User could not be updated'; - if (args.data && args.data.error == 'duplicated_email') { - flash.error = 'User could not be updated - email already in use'; - } else { - flash.error = 'User could not be updated'; - } - }); - }; - $scope.deleteUser = function() { - if (confirm('Do you really want to remove this user?')) { - Restangular.one('users', $scope.user.uid).remove().then( - function() { - var index = findByAttr($scope.users, 'uid', $routeParams.userId); - - if (index !== false) { - $scope.users = $scope.users.splice(index, 1); - } - - // Remove from all groups - var i, - len = $scope.groups.length; - - for (i=0; i < len; i++) { - $scope.groups[i].users = _.without($scope.groups[i].users, $routeParams.userId); - } - - window.history.back(); - flash.success = 'User correctly removed'; - }, - function errorCallback() { - flash.error = 'Oops error from server :('; - } - ); - } - }; - $scope.isClean = function() { - return angular.equals(remote, $scope.user); - }; - $scope.selectGroup = function(group) { - group.hasUsers = !group.hasUsers; - // check whether the list of groups changed - $scope.groupsChanged = !angular.equals($scope.original_groups, $scope.user_groups); - }; - // we wan't to initialize groups when the groups button is clicked - $scope.initGroups = function() { - // A copy of list of groups (w/ information on whether the user is part - // of this group or not - $scope.user_groups = angular.copy($scope.groups); - - var tree = []; - var prefix; - angular.forEach($scope.user_groups, function(group, key) { - addNode(tree, group); - }); - $scope.user_groups_tree = tree; - - angular.forEach($scope.user_groups, function(group, key) { - group.hasUsers = _.contains(group.users, $scope.user.uid); - }); - - $scope.original_groups = angular.copy($scope.user_groups); - $scope.groupsChanged = false; - }; - - // called when user submits modifications on groups list for a user - $scope.apply = function() { - postGroups($scope, $scope.user, Restangular, flash); - }; - }); - }) - .controller('UserCreateCtrl', function($scope, Restangular, flash) { - $scope.publicContextPath = GEOR_config.publicContextPath; - $scope.save = function() { - Restangular.all('users').post( - $scope.user - ).then(function(user) { - $scope.users.push(user); - window.location = "#/users"; - flash.success = 'User correctly added'; - }, - function errorCallback(response) { - if (response.status == 409) { - flash.error = 'Error while creating the user: is the specified e-mail already used ?'; - } else { - flash.error = 'Error while creating the user'; - } - }); - }; - }) - .controller('FooCtrl', function($scope) { - $scope.foo = "bar"; - }); - -function getPrefix(group) { - return group.cn.indexOf('_') != -1 && - group.cn.substring(0, group.cn.indexOf('_')); -} - -function findByAttr(collection, attribute, value) { - var i, - len = collection.length; - for (i = 0; i < len; i++) { - if (collection[i][attribute] == value) { - return i; - } - } - return false; -} - -function postGroups($scope, users, Restangular, flash) { - var i, - len = $scope.user_groups.length, - toPut = [], - toDelete = []; - users = _.isArray(users) ? users : [users]; - users = _.pluck(users, 'uid'); - - // get the list of groups to put or delete for user - for (i=0; i < len; i++) { - var g = $scope.user_groups[i], - og = $scope.original_groups[i]; - - if (g.hasUsers != og.hasUsers) { - if (g.hasUsers === 'all' || g.hasUsers === true) { - toPut.push(g.cn); - } else if (g.hasUsers === false){ - toDelete.push(g.cn); - } - // 'some' shouldn't be possible here - } - } - - // because the number of users can be important (for example when checkAll - // checkbox is checked), body is less heavy this way - var body = { - "users": users, - "PUT": toPut, - "DELETE": toDelete - }; - - Restangular.all('groups_users').post(body).then( - function() { - angular.forEach(toPut, function(group) { - var index = findByAttr($scope.groups, 'cn', group); - group = $scope.groups[index]; - group.users = _.union(group.users || [], users); - }); - angular.forEach(toDelete, function(group) { - var index = findByAttr($scope.groups, 'cn', group); - group = $scope.groups[index]; - group.users = _.difference(group.users, users); - }); - - flash.success = 'Modified successfully'; - - var index = findByAttr($scope.groups, 'cn', 'a'); - var group = $scope.groups[index]; - }, - function errorCallback() { - flash.error = 'Oops error from server :('; - } - ); -} - -function addNode(tree, node) { - var prefix = getPrefix(node); - - if (prefix) { - // creating a branch - var branch = _.find(tree, function(obj) {return obj.name == prefix;}); - if (!branch) { - branch = {name: prefix, nodes: []}; - tree.push(branch); - tree.sort(function(a, b) { - if (a.name.toLowerCase() > b.name.toLowerCase()) { - return 1; - } else { - return -1; - } - }); - } - branch.nodes.push({group: node}); - branch.nodes.sort(function(a, b) { - if (a.group.cn.toLowerCase() > b.group.cn.toLowerCase()) { - return 1; - } else { - return -1; - } - }); - } else { - tree.push({name: node.cn, group: node}); - } -} - -function removeNode(tree, nodeToRemove) { - function loop(nodes) { - angular.forEach(nodes, function(node, ndx) { - if (node.nodes) { - loop(node.nodes); - } else { - if (node.group.cn == nodeToRemove) { - nodes = nodes.splice(ndx, 1); - } - } - }); - } - loop(tree, nodeToRemove); -} - -$(document) - .on('click.dropdown-menu', '.dropdown-menu > li.noclose', function (e) { - e.stopPropagation(); - }); -$('.groups').delegate('.accordion', 'show hide', function (n) { - $(n.target).siblings('.accordion-heading').find('.accordion-toggle i').toggleClass('icon-chevron-down icon-chevron-right'); -}); diff --git a/ldapadmin/src/main/webapp/privateui/js/directives.js b/ldapadmin/src/main/webapp/privateui/js/directives.js deleted file mode 100644 index dd36c42212..0000000000 --- a/ldapadmin/src/main/webapp/privateui/js/directives.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -/* Directives */ - - -//angular.module('ldapadmin.directives', []). - //directive('appVersion', ['version', function(version) { - //return function(scope, elm, attrs) { - //elm.text(version); - //}; - //}]);j -angular.module('ldapadmin.directives', []). - directive('ldapadminCheckAll', function() { - // scope should expose 'allSelected' and 'allClear' - return function(scope, elm, attrs) { - scope.$watch(attrs.ldapadminCheckAll, function(value) { - elm.prop('indeterminate', value); - }, true); - }; - }) - .directive('groupsDropdown', function() { - return { - replace: true, - templateUrl: "partials/groups_dropdown.html" - }; - }); diff --git a/ldapadmin/src/main/webapp/privateui/js/filters.js b/ldapadmin/src/main/webapp/privateui/js/filters.js deleted file mode 100644 index 7a51e42044..0000000000 --- a/ldapadmin/src/main/webapp/privateui/js/filters.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; - -/* Filters */ - -angular.module('ldapadmin.filters', []). - filter('count', [function() { - return function(users, group) { - if (users) { - var i, - len = users.length, - count = 0; - for (i = 0; i < len; i++) { - if (users[i].groups) { - count += users[i].groups.indexOf(group) != -1 ? 1 : 0; - } - } - return count; - } - }; - }]); - //filter('interpolate', ['version', function(version) { - //return function(text) { - //return String(text).replace(/\%VERSION\%/mg, version); - //} - //}]); diff --git a/ldapadmin/src/main/webapp/privateui/js/services.js b/ldapadmin/src/main/webapp/privateui/js/services.js deleted file mode 100644 index eb109abbed..0000000000 --- a/ldapadmin/src/main/webapp/privateui/js/services.js +++ /dev/null @@ -1,2 +0,0 @@ -'use strict'; - diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular-flash.min.js b/ldapadmin/src/main/webapp/privateui/lib/angular-flash.min.js deleted file mode 100644 index 2fd8a5cdf3..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular-flash.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/**! - * @license angular-flash v0.1.4 - * Copyright (c) 2013 William L. Bunselmeyer. https://github.com/wmluke/angular-flash - * License: MIT - */ -!function(){"use strict";function a(){return new b}var b=function(){function a(a,b){angular.forEach(h,function(c){c.type&&c.type!==a||c.cb(b,a)})}var b,c,d,e,f,g=this,h=[];this.clean=function(){h=[],b=null,c=null,d=null,e=null,f=null},this.subscribe=function(a,b){h.push({cb:a,type:b})},Object.defineProperty(this,"success",{get:function(){return b},set:function(c){b=c,f="success",a(f,c)}}),Object.defineProperty(this,"info",{get:function(){return c},set:function(b){c=b,f="info",a(f,b)}}),Object.defineProperty(this,"warn",{get:function(){return d},set:function(b){d=b,f="warn",a(f,b)}}),Object.defineProperty(this,"error",{get:function(){return e},set:function(b){e=b,f="error",a(f,b)}}),Object.defineProperty(this,"type",{get:function(){return f}}),Object.defineProperty(this,"message",{get:function(){return f?g[f]:null}})};angular.module("angular-flash.service",[]).factory("flash",[a])}(),function(){"use strict";function a(a){return(null===a||void 0===a)&&(a=""),/^\s*$/.test(a)}function b(b,c){return{scope:!0,link:function(d,e,f){function g(){e.removeClass("alert-info"),e.removeClass("alert-warn"),e.removeClass("alert-error"),e.removeClass("alert-success")}function h(){d.flash={},g(),a(f.activeClass)||e.removeClass(f.activeClass)}function i(b,i){j&&c.cancel(j),d.flash.type=i,d.flash.message=b,g(),e.addClass("alert-"+i),a(f.activeClass)||e.addClass(f.activeClass),j=c(h,5e3)}var j;d.flash={},b.subscribe(i,f.flashAlert),f.flashAlert&&b[f.flashAlert]&&i(b[f.flashAlert],f.flashAlert),!f.flashAlert&&b.message&&i(b.message,b.type)}}}angular.module("angular-flash.flash-alert-directive",["angular-flash.service"]).directive("flashAlert",["flash","$timeout",b])}(); \ No newline at end of file diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-cookies.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-cookies.js deleted file mode 100644 index 78686e2378..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-cookies.js +++ /dev/null @@ -1,185 +0,0 @@ -/** - * @license AngularJS v1.1.5 - * (c) 2010-2012 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, angular, undefined) { -'use strict'; - -/** - * @ngdoc overview - * @name ngCookies - */ - - -angular.module('ngCookies', ['ng']). - /** - * @ngdoc object - * @name ngCookies.$cookies - * @requires $browser - * - * @description - * Provides read/write access to browser's cookies. - * - * Only a simple Object is exposed and by adding or removing properties to/from - * this object, new cookies are created/deleted at the end of current $eval. - * - * @example - - - - - - */ - factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) { - var cookies = {}, - lastCookies = {}, - lastBrowserCookies, - runEval = false, - copy = angular.copy, - isUndefined = angular.isUndefined; - - //creates a poller fn that copies all cookies from the $browser to service & inits the service - $browser.addPollFn(function() { - var currentCookies = $browser.cookies(); - if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl - lastBrowserCookies = currentCookies; - copy(currentCookies, lastCookies); - copy(currentCookies, cookies); - if (runEval) $rootScope.$apply(); - } - })(); - - runEval = true; - - //at the end of each eval, push cookies - //TODO: this should happen before the "delayed" watches fire, because if some cookies are not - // strings or browser refuses to store some cookies, we update the model in the push fn. - $rootScope.$watch(push); - - return cookies; - - - /** - * Pushes all the cookies from the service to the browser and verifies if all cookies were stored. - */ - function push() { - var name, - value, - browserCookies, - updated; - - //delete any cookies deleted in $cookies - for (name in lastCookies) { - if (isUndefined(cookies[name])) { - $browser.cookies(name, undefined); - } - } - - //update all cookies updated in $cookies - for(name in cookies) { - value = cookies[name]; - if (!angular.isString(value)) { - if (angular.isDefined(lastCookies[name])) { - cookies[name] = lastCookies[name]; - } else { - delete cookies[name]; - } - } else if (value !== lastCookies[name]) { - $browser.cookies(name, value); - updated = true; - } - } - - //verify what was actually stored - if (updated){ - updated = false; - browserCookies = $browser.cookies(); - - for (name in cookies) { - if (cookies[name] !== browserCookies[name]) { - //delete or reset all cookies that the browser dropped from $cookies - if (isUndefined(browserCookies[name])) { - delete cookies[name]; - } else { - cookies[name] = browserCookies[name]; - } - updated = true; - } - } - } - } - }]). - - - /** - * @ngdoc object - * @name ngCookies.$cookieStore - * @requires $cookies - * - * @description - * Provides a key-value (string-object) storage, that is backed by session cookies. - * Objects put or retrieved from this storage are automatically serialized or - * deserialized by angular's toJson/fromJson. - * @example - */ - factory('$cookieStore', ['$cookies', function($cookies) { - - return { - /** - * @ngdoc method - * @name ngCookies.$cookieStore#get - * @methodOf ngCookies.$cookieStore - * - * @description - * Returns the value of given cookie key - * - * @param {string} key Id to use for lookup. - * @returns {Object} Deserialized cookie value. - */ - get: function(key) { - var value = $cookies[key]; - return value ? angular.fromJson(value) : value; - }, - - /** - * @ngdoc method - * @name ngCookies.$cookieStore#put - * @methodOf ngCookies.$cookieStore - * - * @description - * Sets a value for given cookie key - * - * @param {string} key Id for the `value`. - * @param {Object} value Value to be stored. - */ - put: function(key, value) { - $cookies[key] = angular.toJson(value); - }, - - /** - * @ngdoc method - * @name ngCookies.$cookieStore#remove - * @methodOf ngCookies.$cookieStore - * - * @description - * Remove given cookie - * - * @param {string} key Id of the key-value pair to delete. - */ - remove: function(key) { - delete $cookies[key]; - } - }; - - }]); - - -})(window, window.angular); diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-cookies.min.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-cookies.min.js deleted file mode 100644 index 9d9082604a..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-cookies.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - AngularJS v1.1.5 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(m,f,l){'use strict';f.module("ngCookies",["ng"]).factory("$cookies",["$rootScope","$browser",function(d,b){var c={},g={},h,i=!1,j=f.copy,k=f.isUndefined;b.addPollFn(function(){var a=b.cookies();h!=a&&(h=a,j(a,g),j(a,c),i&&d.$apply())})();i=!0;d.$watch(function(){var a,e,d;for(a in g)k(c[a])&&b.cookies(a,l);for(a in c)e=c[a],f.isString(e)?e!==g[a]&&(b.cookies(a,e),d=!0):f.isDefined(g[a])?c[a]=g[a]:delete c[a];if(d)for(a in e=b.cookies(),c)c[a]!==e[a]&&(k(e[a])?delete c[a]:c[a]=e[a])});return c}]).factory("$cookieStore", -["$cookies",function(d){return{get:function(b){return(b=d[b])?f.fromJson(b):b},put:function(b,c){d[b]=f.toJson(c)},remove:function(b){delete d[b]}}}])})(window,window.angular); diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-loader.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-loader.js deleted file mode 100644 index 4d7184f04a..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-loader.js +++ /dev/null @@ -1,304 +0,0 @@ -/** - * @license AngularJS v1.1.5 - * (c) 2010-2012 Google, Inc. http://angularjs.org - * License: MIT - */ - -( - -/** - * @ngdoc interface - * @name angular.Module - * @description - * - * Interface for configuring angular {@link angular.module modules}. - */ - -function setupModuleLoader(window) { - - function ensure(obj, name, factory) { - return obj[name] || (obj[name] = factory()); - } - - return ensure(ensure(window, 'angular', Object), 'module', function() { - /** @type {Object.} */ - var modules = {}; - - /** - * @ngdoc function - * @name angular.module - * @description - * - * The `angular.module` is a global place for creating and registering Angular modules. All - * modules (angular core or 3rd party) that should be available to an application must be - * registered using this mechanism. - * - * - * # Module - * - * A module is a collocation of services, directives, filters, and configuration information. Module - * is used to configure the {@link AUTO.$injector $injector}. - * - *
-     * // Create a new module
-     * var myModule = angular.module('myModule', []);
-     *
-     * // register a new service
-     * myModule.value('appName', 'MyCoolApp');
-     *
-     * // configure existing services inside initialization blocks.
-     * myModule.config(function($locationProvider) {
-'use strict';
-     *   // Configure existing providers
-     *   $locationProvider.hashPrefix('!');
-     * });
-     * 
- * - * Then you can create an injector and load your modules like this: - * - *
-     * var injector = angular.injector(['ng', 'MyModule'])
-     * 
- * - * However it's more likely that you'll just use - * {@link ng.directive:ngApp ngApp} or - * {@link angular.bootstrap} to simplify this process for you. - * - * @param {!string} name The name of the module to create or retrieve. - * @param {Array.=} requires If specified then new module is being created. If unspecified then the - * the module is being retrieved for further configuration. - * @param {Function} configFn Optional configuration function for the module. Same as - * {@link angular.Module#config Module#config()}. - * @returns {module} new module with the {@link angular.Module} api. - */ - return function module(name, requires, configFn) { - if (requires && modules.hasOwnProperty(name)) { - modules[name] = null; - } - return ensure(modules, name, function() { - if (!requires) { - throw Error('No module: ' + name); - } - - /** @type {!Array.>} */ - var invokeQueue = []; - - /** @type {!Array.} */ - var runBlocks = []; - - var config = invokeLater('$injector', 'invoke'); - - /** @type {angular.Module} */ - var moduleInstance = { - // Private state - _invokeQueue: invokeQueue, - _runBlocks: runBlocks, - - /** - * @ngdoc property - * @name angular.Module#requires - * @propertyOf angular.Module - * @returns {Array.} List of module names which must be loaded before this module. - * @description - * Holds the list of modules which the injector will load before the current module is loaded. - */ - requires: requires, - - /** - * @ngdoc property - * @name angular.Module#name - * @propertyOf angular.Module - * @returns {string} Name of the module. - * @description - */ - name: name, - - - /** - * @ngdoc method - * @name angular.Module#provider - * @methodOf angular.Module - * @param {string} name service name - * @param {Function} providerType Construction function for creating new instance of the service. - * @description - * See {@link AUTO.$provide#provider $provide.provider()}. - */ - provider: invokeLater('$provide', 'provider'), - - /** - * @ngdoc method - * @name angular.Module#factory - * @methodOf angular.Module - * @param {string} name service name - * @param {Function} providerFunction Function for creating new instance of the service. - * @description - * See {@link AUTO.$provide#factory $provide.factory()}. - */ - factory: invokeLater('$provide', 'factory'), - - /** - * @ngdoc method - * @name angular.Module#service - * @methodOf angular.Module - * @param {string} name service name - * @param {Function} constructor A constructor function that will be instantiated. - * @description - * See {@link AUTO.$provide#service $provide.service()}. - */ - service: invokeLater('$provide', 'service'), - - /** - * @ngdoc method - * @name angular.Module#value - * @methodOf angular.Module - * @param {string} name service name - * @param {*} object Service instance object. - * @description - * See {@link AUTO.$provide#value $provide.value()}. - */ - value: invokeLater('$provide', 'value'), - - /** - * @ngdoc method - * @name angular.Module#constant - * @methodOf angular.Module - * @param {string} name constant name - * @param {*} object Constant value. - * @description - * Because the constant are fixed, they get applied before other provide methods. - * See {@link AUTO.$provide#constant $provide.constant()}. - */ - constant: invokeLater('$provide', 'constant', 'unshift'), - - /** - * @ngdoc method - * @name angular.Module#animation - * @methodOf angular.Module - * @param {string} name animation name - * @param {Function} animationFactory Factory function for creating new instance of an animation. - * @description - * - * Defines an animation hook that can be later used with {@link ng.directive:ngAnimate ngAnimate} - * alongside {@link ng.directive:ngAnimate#Description common ng directives} as well as custom directives. - *
-           * module.animation('animation-name', function($inject1, $inject2) {
-           *   return {
-           *     //this gets called in preparation to setup an animation
-           *     setup : function(element) { ... },
-           *
-           *     //this gets called once the animation is run
-           *     start : function(element, done, memo) { ... }
-           *   }
-           * })
-           * 
- * - * See {@link ng.$animationProvider#register $animationProvider.register()} and - * {@link ng.directive:ngAnimate ngAnimate} for more information. - */ - animation: invokeLater('$animationProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#filter - * @methodOf angular.Module - * @param {string} name Filter name. - * @param {Function} filterFactory Factory function for creating new instance of filter. - * @description - * See {@link ng.$filterProvider#register $filterProvider.register()}. - */ - filter: invokeLater('$filterProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#controller - * @methodOf angular.Module - * @param {string} name Controller name. - * @param {Function} constructor Controller constructor function. - * @description - * See {@link ng.$controllerProvider#register $controllerProvider.register()}. - */ - controller: invokeLater('$controllerProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#directive - * @methodOf angular.Module - * @param {string} name directive name - * @param {Function} directiveFactory Factory function for creating new instance of - * directives. - * @description - * See {@link ng.$compileProvider#directive $compileProvider.directive()}. - */ - directive: invokeLater('$compileProvider', 'directive'), - - /** - * @ngdoc method - * @name angular.Module#config - * @methodOf angular.Module - * @param {Function} configFn Execute this function on module load. Useful for service - * configuration. - * @description - * Use this method to register work which needs to be performed on module loading. - */ - config: config, - - /** - * @ngdoc method - * @name angular.Module#run - * @methodOf angular.Module - * @param {Function} initializationFn Execute this function after injector creation. - * Useful for application initialization. - * @description - * Use this method to register work which should be performed when the injector is done - * loading all modules. - */ - run: function(block) { - runBlocks.push(block); - return this; - } - }; - - if (configFn) { - config(configFn); - } - - return moduleInstance; - - /** - * @param {string} provider - * @param {string} method - * @param {String=} insertMethod - * @returns {angular.Module} - */ - function invokeLater(provider, method, insertMethod) { - return function() { - invokeQueue[insertMethod || 'push']([provider, method, arguments]); - return moduleInstance; - } - } - }); - }; - }); - -} - -)(window); - -/** - * Closure compiler type information - * - * @typedef { { - * requires: !Array., - * invokeQueue: !Array.>, - * - * service: function(string, Function):angular.Module, - * factory: function(string, Function):angular.Module, - * value: function(string, *):angular.Module, - * - * filter: function(string, Function):angular.Module, - * - * init: function(Function):angular.Module - * } } - */ -angular.Module; - diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-loader.min.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-loader.min.js deleted file mode 100644 index 0c262d4920..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-loader.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - AngularJS v1.1.5 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(i){'use strict';function d(c,b,e){return c[b]||(c[b]=e())}return d(d(i,"angular",Object),"module",function(){var c={};return function(b,e,f){e&&c.hasOwnProperty(b)&&(c[b]=null);return d(c,b,function(){function a(a,b,d){return function(){c[d||"push"]([a,b,arguments]);return g}}if(!e)throw Error("No module: "+b);var c=[],d=[],h=a("$injector","invoke"),g={_invokeQueue:c,_runBlocks:d,requires:e,name:b,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"), -value:a("$provide","value"),constant:a("$provide","constant","unshift"),animation:a("$animationProvider","register"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:h,run:function(a){d.push(a);return this}};f&&h(f);return g})}})})(window); diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-mobile.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-mobile.js deleted file mode 100644 index bf82d8c2b3..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-mobile.js +++ /dev/null @@ -1,460 +0,0 @@ -/** - * @license AngularJS v1.1.5 - * (c) 2010-2012 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, angular, undefined) { -'use strict'; - -/** - * @ngdoc overview - * @name ngMobile - * @description - * Touch events and other mobile helpers. - * Based on jQuery Mobile touch event handling (jquerymobile.com) - */ - -// define ngMobile module -var ngMobile = angular.module('ngMobile', []); - -/** - * @ngdoc directive - * @name ngMobile.directive:ngClick - * - * @description - * A more powerful replacement for the default ngClick designed to be used on touchscreen - * devices. Most mobile browsers wait about 300ms after a tap-and-release before sending - * the click event. This version handles them immediately, and then prevents the - * following click event from propagating. - * - * This directive can fall back to using an ordinary click event, and so works on desktop - * browsers as well as mobile. - * - * This directive also sets the CSS class `ng-click-active` while the element is being held - * down (by a mouse click or touch) so you can restyle the depressed element if you wish. - * - * @element ANY - * @param {expression} ngClick {@link guide/expression Expression} to evaluate - * upon tap. (Event object is available as `$event`) - * - * @example - - - - count: {{ count }} - - - */ - -ngMobile.config(['$provide', function($provide) { - $provide.decorator('ngClickDirective', ['$delegate', function($delegate) { - // drop the default ngClick directive - $delegate.shift(); - return $delegate; - }]); -}]); - -ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement', - function($parse, $timeout, $rootElement) { - var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag. - var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers. - var PREVENT_DURATION = 2500; // 2.5 seconds maximum from preventGhostClick call to click - var CLICKBUSTER_THRESHOLD = 25; // 25 pixels in any dimension is the limit for busting clicks. - - var ACTIVE_CLASS_NAME = 'ng-click-active'; - var lastPreventedTime; - var touchCoordinates; - - - // TAP EVENTS AND GHOST CLICKS - // - // Why tap events? - // Mobile browsers detect a tap, then wait a moment (usually ~300ms) to see if you're - // double-tapping, and then fire a click event. - // - // This delay sucks and makes mobile apps feel unresponsive. - // So we detect touchstart, touchmove, touchcancel and touchend ourselves and determine when - // the user has tapped on something. - // - // What happens when the browser then generates a click event? - // The browser, of course, also detects the tap and fires a click after a delay. This results in - // tapping/clicking twice. So we do "clickbusting" to prevent it. - // - // How does it work? - // We attach global touchstart and click handlers, that run during the capture (early) phase. - // So the sequence for a tap is: - // - global touchstart: Sets an "allowable region" at the point touched. - // - element's touchstart: Starts a touch - // (- touchmove or touchcancel ends the touch, no click follows) - // - element's touchend: Determines if the tap is valid (didn't move too far away, didn't hold - // too long) and fires the user's tap handler. The touchend also calls preventGhostClick(). - // - preventGhostClick() removes the allowable region the global touchstart created. - // - The browser generates a click event. - // - The global click handler catches the click, and checks whether it was in an allowable region. - // - If preventGhostClick was called, the region will have been removed, the click is busted. - // - If the region is still there, the click proceeds normally. Therefore clicks on links and - // other elements without ngTap on them work normally. - // - // This is an ugly, terrible hack! - // Yeah, tell me about it. The alternatives are using the slow click events, or making our users - // deal with the ghost clicks, so I consider this the least of evils. Fortunately Angular - // encapsulates this ugly logic away from the user. - // - // Why not just put click handlers on the element? - // We do that too, just to be sure. The problem is that the tap event might have caused the DOM - // to change, so that the click fires in the same position but something else is there now. So - // the handlers are global and care only about coordinates and not elements. - - // Checks if the coordinates are close enough to be within the region. - function hit(x1, y1, x2, y2) { - return Math.abs(x1 - x2) < CLICKBUSTER_THRESHOLD && Math.abs(y1 - y2) < CLICKBUSTER_THRESHOLD; - } - - // Checks a list of allowable regions against a click location. - // Returns true if the click should be allowed. - // Splices out the allowable region from the list after it has been used. - function checkAllowableRegions(touchCoordinates, x, y) { - for (var i = 0; i < touchCoordinates.length; i += 2) { - if (hit(touchCoordinates[i], touchCoordinates[i+1], x, y)) { - touchCoordinates.splice(i, i + 2); - return true; // allowable region - } - } - return false; // No allowable region; bust it. - } - - // Global click handler that prevents the click if it's in a bustable zone and preventGhostClick - // was called recently. - function onClick(event) { - if (Date.now() - lastPreventedTime > PREVENT_DURATION) { - return; // Too old. - } - - var touches = event.touches && event.touches.length ? event.touches : [event]; - var x = touches[0].clientX; - var y = touches[0].clientY; - // Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label - // and on the input element). Depending on the exact browser, this second click we don't want - // to bust has either (0,0) or negative coordinates. - if (x < 1 && y < 1) { - return; // offscreen - } - - // Look for an allowable region containing this click. - // If we find one, that means it was created by touchstart and not removed by - // preventGhostClick, so we don't bust it. - if (checkAllowableRegions(touchCoordinates, x, y)) { - return; - } - - // If we didn't find an allowable region, bust the click. - event.stopPropagation(); - event.preventDefault(); - } - - - // Global touchstart handler that creates an allowable region for a click event. - // This allowable region can be removed by preventGhostClick if we want to bust it. - function onTouchStart(event) { - var touches = event.touches && event.touches.length ? event.touches : [event]; - var x = touches[0].clientX; - var y = touches[0].clientY; - touchCoordinates.push(x, y); - - $timeout(function() { - // Remove the allowable region. - for (var i = 0; i < touchCoordinates.length; i += 2) { - if (touchCoordinates[i] == x && touchCoordinates[i+1] == y) { - touchCoordinates.splice(i, i + 2); - return; - } - } - }, PREVENT_DURATION, false); - } - - // On the first call, attaches some event handlers. Then whenever it gets called, it creates a - // zone around the touchstart where clicks will get busted. - function preventGhostClick(x, y) { - if (!touchCoordinates) { - $rootElement[0].addEventListener('click', onClick, true); - $rootElement[0].addEventListener('touchstart', onTouchStart, true); - touchCoordinates = []; - } - - lastPreventedTime = Date.now(); - - checkAllowableRegions(touchCoordinates, x, y); - } - - // Actual linking function. - return function(scope, element, attr) { - var clickHandler = $parse(attr.ngClick), - tapping = false, - tapElement, // Used to blur the element after a tap. - startTime, // Used to check if the tap was held too long. - touchStartX, - touchStartY; - - function resetState() { - tapping = false; - element.removeClass(ACTIVE_CLASS_NAME); - } - - element.bind('touchstart', function(event) { - tapping = true; - tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement. - // Hack for Safari, which can target text nodes instead of containers. - if(tapElement.nodeType == 3) { - tapElement = tapElement.parentNode; - } - - element.addClass(ACTIVE_CLASS_NAME); - - startTime = Date.now(); - - var touches = event.touches && event.touches.length ? event.touches : [event]; - var e = touches[0].originalEvent || touches[0]; - touchStartX = e.clientX; - touchStartY = e.clientY; - }); - - element.bind('touchmove', function(event) { - resetState(); - }); - - element.bind('touchcancel', function(event) { - resetState(); - }); - - element.bind('touchend', function(event) { - var diff = Date.now() - startTime; - - var touches = (event.changedTouches && event.changedTouches.length) ? event.changedTouches : - ((event.touches && event.touches.length) ? event.touches : [event]); - var e = touches[0].originalEvent || touches[0]; - var x = e.clientX; - var y = e.clientY; - var dist = Math.sqrt( Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2) ); - - if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) { - // Call preventGhostClick so the clickbuster will catch the corresponding click. - preventGhostClick(x, y); - - // Blur the focused element (the button, probably) before firing the callback. - // This doesn't work perfectly on Android Chrome, but seems to work elsewhere. - // I couldn't get anything to work reliably on Android Chrome. - if (tapElement) { - tapElement.blur(); - } - - scope.$apply(function() { - // TODO(braden): This is sending the touchend, not a tap or click. Is that kosher? - clickHandler(scope, {$event: event}); - }); - } - - resetState(); - }); - - // Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click - // something else nearby. - element.onclick = function(event) { }; - - // Fallback click handler. - // Busted clicks don't get this far, and adding this handler allows ng-tap to be used on - // desktop as well, to allow more portable sites. - element.bind('click', function(event) { - scope.$apply(function() { - clickHandler(scope, {$event: event}); - }); - }); - - element.bind('mousedown', function(event) { - element.addClass(ACTIVE_CLASS_NAME); - }); - - element.bind('mousemove mouseup', function(event) { - element.removeClass(ACTIVE_CLASS_NAME); - }); - - }; -}]); - -/** - * @ngdoc directive - * @name ngMobile.directive:ngSwipeLeft - * - * @description - * Specify custom behavior when an element is swiped to the left on a touchscreen device. - * A leftward swipe is a quick, right-to-left slide of the finger. - * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag too. - * - * @element ANY - * @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate - * upon left swipe. (Event object is available as `$event`) - * - * @example - - -
- Some list content, like an email in the inbox -
-
- - -
-
-
- */ - -/** - * @ngdoc directive - * @name ngMobile.directive:ngSwipeRight - * - * @description - * Specify custom behavior when an element is swiped to the right on a touchscreen device. - * A rightward swipe is a quick, left-to-right slide of the finger. - * Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag too. - * - * @element ANY - * @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate - * upon right swipe. (Event object is available as `$event`) - * - * @example - - -
- Some list content, like an email in the inbox -
-
- - -
-
-
- */ - -function makeSwipeDirective(directiveName, direction) { - ngMobile.directive(directiveName, ['$parse', function($parse) { - // The maximum vertical delta for a swipe should be less than 75px. - var MAX_VERTICAL_DISTANCE = 75; - // Vertical distance should not be more than a fraction of the horizontal distance. - var MAX_VERTICAL_RATIO = 0.3; - // At least a 30px lateral motion is necessary for a swipe. - var MIN_HORIZONTAL_DISTANCE = 30; - // The total distance in any direction before we make the call on swipe vs. scroll. - var MOVE_BUFFER_RADIUS = 10; - - function getCoordinates(event) { - var touches = event.touches && event.touches.length ? event.touches : [event]; - var e = (event.changedTouches && event.changedTouches[0]) || - (event.originalEvent && event.originalEvent.changedTouches && - event.originalEvent.changedTouches[0]) || - touches[0].originalEvent || touches[0]; - - return { - x: e.clientX, - y: e.clientY - }; - } - - return function(scope, element, attr) { - var swipeHandler = $parse(attr[directiveName]); - var startCoords, valid; - var totalX, totalY; - var lastX, lastY; - - function validSwipe(event) { - // Check that it's within the coordinates. - // Absolute vertical distance must be within tolerances. - // Horizontal distance, we take the current X - the starting X. - // This is negative for leftward swipes and positive for rightward swipes. - // After multiplying by the direction (-1 for left, +1 for right), legal swipes - // (ie. same direction as the directive wants) will have a positive delta and - // illegal ones a negative delta. - // Therefore this delta must be positive, and larger than the minimum. - if (!startCoords) return false; - var coords = getCoordinates(event); - var deltaY = Math.abs(coords.y - startCoords.y); - var deltaX = (coords.x - startCoords.x) * direction; - return valid && // Short circuit for already-invalidated swipes. - deltaY < MAX_VERTICAL_DISTANCE && - deltaX > 0 && - deltaX > MIN_HORIZONTAL_DISTANCE && - deltaY / deltaX < MAX_VERTICAL_RATIO; - } - - element.bind('touchstart mousedown', function(event) { - startCoords = getCoordinates(event); - valid = true; - totalX = 0; - totalY = 0; - lastX = startCoords.x; - lastY = startCoords.y; - }); - - element.bind('touchcancel', function(event) { - valid = false; - }); - - element.bind('touchmove mousemove', function(event) { - if (!valid) return; - - // Android will send a touchcancel if it thinks we're starting to scroll. - // So when the total distance (+ or - or both) exceeds 10px in either direction, - // we either: - // - On totalX > totalY, we send preventDefault() and treat this as a swipe. - // - On totalY > totalX, we let the browser handle it as a scroll. - - // Invalidate a touch while it's in progress if it strays too far away vertically. - // We don't want a scroll down and back up while drifting sideways to be a swipe just - // because you happened to end up vertically close in the end. - if (!startCoords) return; - var coords = getCoordinates(event); - - if (Math.abs(coords.y - startCoords.y) > MAX_VERTICAL_DISTANCE) { - valid = false; - return; - } - - totalX += Math.abs(coords.x - lastX); - totalY += Math.abs(coords.y - lastY); - - lastX = coords.x; - lastY = coords.y; - - if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) { - return; - } - - // One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll. - if (totalY > totalX) { - valid = false; - return; - } else { - event.preventDefault(); - } - }); - - element.bind('touchend mouseup', function(event) { - if (validSwipe(event)) { - // Prevent this swipe from bubbling up to any other elements with ngSwipes. - event.stopPropagation(); - scope.$apply(function() { - swipeHandler(scope, {$event:event}); - }); - } - }); - }; - }]); -} - -// Left is negative X-coordinate, right is positive. -makeSwipeDirective('ngSwipeLeft', -1); -makeSwipeDirective('ngSwipeRight', 1); - - - -})(window, window.angular); diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-mobile.min.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-mobile.min.js deleted file mode 100644 index bc777ea387..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-mobile.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - AngularJS v1.1.5 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(u,s){'use strict';function k(i,t){j.directive(i,["$parse",function(l){function g(b){var h=b.touches&&b.touches.length?b.touches:[b],b=b.changedTouches&&b.changedTouches[0]||b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]||h[0].originalEvent||h[0];return{x:b.clientX,y:b.clientY}}var m=75,j=0.3,p=30;return function(b,h,n){function o(e){if(!a)return!1;var b=g(e),e=Math.abs(b.y-a.y),b=(b.x-a.x)*t;return c&&e0&&b>p&&e/bm?c=!1:(f+=Math.abs(d.x-q),e+=Math.abs(d.y-r),q=d.x,r=d.y,f<10&&e<10||(e>f?c=!1:b.preventDefault()))}});h.bind("touchend mouseup",function(a){o(a)&&(a.stopPropagation(),b.$apply(function(){d(b,{$event:a})}))})}}])}var j=s.module("ngMobile",[]);j.config(["$provide",function(i){i.decorator("ngClickDirective",["$delegate",function(i){i.shift(); -return i}])}]);j.directive("ngClick",["$parse","$timeout","$rootElement",function(i,j,l){function g(a,c,b){for(var e=0;eb)){var c=a.touches&&a.touches.length?a.touches:[a],f=c[0].clientX,c=c[0].clientY;!(f<1&&c<1)&&!g(d,f,c)&&(a.stopPropagation(),a.preventDefault())}}function k(a){var a=a.touches&&a.touches.length?a.touches:[a],c=a[0].clientX,f=a[0].clientY;d.push(c,f);j(function(){for(var a= -0;a - * describe('$exceptionHandlerProvider', function() { - * - * it('should capture log messages and exceptions', function() { - * - * module(function($exceptionHandlerProvider) { - * $exceptionHandlerProvider.mode('log'); - * }); - * - * inject(function($log, $exceptionHandler, $timeout) { - * $timeout(function() { $log.log(1); }); - * $timeout(function() { $log.log(2); throw 'banana peel'; }); - * $timeout(function() { $log.log(3); }); - * expect($exceptionHandler.errors).toEqual([]); - * expect($log.assertEmpty()); - * $timeout.flush(); - * expect($exceptionHandler.errors).toEqual(['banana peel']); - * expect($log.log.logs).toEqual([[1], [2], [3]]); - * }); - * }); - * }); - * - */ - -angular.mock.$ExceptionHandlerProvider = function() { - var handler; - - /** - * @ngdoc method - * @name ngMock.$exceptionHandlerProvider#mode - * @methodOf ngMock.$exceptionHandlerProvider - * - * @description - * Sets the logging mode. - * - * @param {string} mode Mode of operation, defaults to `rethrow`. - * - * - `rethrow`: If any errors are passed into the handler in tests, it typically - * means that there is a bug in the application or test, so this mock will - * make these tests fail. - * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log` mode stores an - * array of errors in `$exceptionHandler.errors`, to allow later assertion of them. - * See {@link ngMock.$log#assertEmpty assertEmpty()} and - * {@link ngMock.$log#reset reset()} - */ - this.mode = function(mode) { - switch(mode) { - case 'rethrow': - handler = function(e) { - throw e; - }; - break; - case 'log': - var errors = []; - - handler = function(e) { - if (arguments.length == 1) { - errors.push(e); - } else { - errors.push([].slice.call(arguments, 0)); - } - }; - - handler.errors = errors; - break; - default: - throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!"); - } - }; - - this.$get = function() { - return handler; - }; - - this.mode('rethrow'); -}; - - -/** - * @ngdoc service - * @name ngMock.$log - * - * @description - * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays - * (one array per logging level). These arrays are exposed as `logs` property of each of the - * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`. - * - */ -angular.mock.$LogProvider = function() { - - function concat(array1, array2, index) { - return array1.concat(Array.prototype.slice.call(array2, index)); - } - - - this.$get = function () { - var $log = { - log: function() { $log.log.logs.push(concat([], arguments, 0)); }, - warn: function() { $log.warn.logs.push(concat([], arguments, 0)); }, - info: function() { $log.info.logs.push(concat([], arguments, 0)); }, - error: function() { $log.error.logs.push(concat([], arguments, 0)); } - }; - - /** - * @ngdoc method - * @name ngMock.$log#reset - * @methodOf ngMock.$log - * - * @description - * Reset all of the logging arrays to empty. - */ - $log.reset = function () { - /** - * @ngdoc property - * @name ngMock.$log#log.logs - * @propertyOf ngMock.$log - * - * @description - * Array of messages logged using {@link ngMock.$log#log}. - * - * @example - *
-       * $log.log('Some Log');
-       * var first = $log.log.logs.unshift();
-       * 
- */ - $log.log.logs = []; - /** - * @ngdoc property - * @name ngMock.$log#warn.logs - * @propertyOf ngMock.$log - * - * @description - * Array of messages logged using {@link ngMock.$log#warn}. - * - * @example - *
-       * $log.warn('Some Warning');
-       * var first = $log.warn.logs.unshift();
-       * 
- */ - $log.warn.logs = []; - /** - * @ngdoc property - * @name ngMock.$log#info.logs - * @propertyOf ngMock.$log - * - * @description - * Array of messages logged using {@link ngMock.$log#info}. - * - * @example - *
-       * $log.info('Some Info');
-       * var first = $log.info.logs.unshift();
-       * 
- */ - $log.info.logs = []; - /** - * @ngdoc property - * @name ngMock.$log#error.logs - * @propertyOf ngMock.$log - * - * @description - * Array of messages logged using {@link ngMock.$log#error}. - * - * @example - *
-       * $log.log('Some Error');
-       * var first = $log.error.logs.unshift();
-       * 
- */ - $log.error.logs = []; - }; - - /** - * @ngdoc method - * @name ngMock.$log#assertEmpty - * @methodOf ngMock.$log - * - * @description - * Assert that the all of the logging methods have no logged messages. If messages present, an exception is thrown. - */ - $log.assertEmpty = function() { - var errors = []; - angular.forEach(['error', 'warn', 'info', 'log'], function(logLevel) { - angular.forEach($log[logLevel].logs, function(log) { - angular.forEach(log, function (logItem) { - errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + (logItem.stack || '')); - }); - }); - }); - if (errors.length) { - errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or an expected " + - "log message was not checked and removed:"); - errors.push(''); - throw new Error(errors.join('\n---------\n')); - } - }; - - $log.reset(); - return $log; - }; -}; - - -(function() { - var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; - - function jsonStringToDate(string){ - var match; - if (match = string.match(R_ISO8061_STR)) { - var date = new Date(0), - tzHour = 0, - tzMin = 0; - if (match[9]) { - tzHour = int(match[9] + match[10]); - tzMin = int(match[9] + match[11]); - } - date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); - date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0)); - return date; - } - return string; - } - - function int(str) { - return parseInt(str, 10); - } - - function padNumber(num, digits, trim) { - var neg = ''; - if (num < 0) { - neg = '-'; - num = -num; - } - num = '' + num; - while(num.length < digits) num = '0' + num; - if (trim) - num = num.substr(num.length - digits); - return neg + num; - } - - - /** - * @ngdoc object - * @name angular.mock.TzDate - * @description - * - * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. - * - * Mock of the Date type which has its timezone specified via constructor arg. - * - * The main purpose is to create Date-like instances with timezone fixed to the specified timezone - * offset, so that we can test code that depends on local timezone settings without dependency on - * the time zone settings of the machine where the code is running. - * - * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored) - * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* - * - * @example - * !!!! WARNING !!!!! - * This is not a complete Date object so only methods that were implemented can be called safely. - * To make matters worse, TzDate instances inherit stuff from Date via a prototype. - * - * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is - * incomplete we might be missing some non-standard methods. This can result in errors like: - * "Date.prototype.foo called on incompatible Object". - * - *
-   * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
-   * newYearInBratislava.getTimezoneOffset() => -60;
-   * newYearInBratislava.getFullYear() => 2010;
-   * newYearInBratislava.getMonth() => 0;
-   * newYearInBratislava.getDate() => 1;
-   * newYearInBratislava.getHours() => 0;
-   * newYearInBratislava.getMinutes() => 0;
-   * newYearInBratislava.getSeconds() => 0;
-   * 
- * - */ - angular.mock.TzDate = function (offset, timestamp) { - var self = new Date(0); - if (angular.isString(timestamp)) { - var tsStr = timestamp; - - self.origDate = jsonStringToDate(timestamp); - - timestamp = self.origDate.getTime(); - if (isNaN(timestamp)) - throw { - name: "Illegal Argument", - message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string" - }; - } else { - self.origDate = new Date(timestamp); - } - - var localOffset = new Date(timestamp).getTimezoneOffset(); - self.offsetDiff = localOffset*60*1000 - offset*1000*60*60; - self.date = new Date(timestamp + self.offsetDiff); - - self.getTime = function() { - return self.date.getTime() - self.offsetDiff; - }; - - self.toLocaleDateString = function() { - return self.date.toLocaleDateString(); - }; - - self.getFullYear = function() { - return self.date.getFullYear(); - }; - - self.getMonth = function() { - return self.date.getMonth(); - }; - - self.getDate = function() { - return self.date.getDate(); - }; - - self.getHours = function() { - return self.date.getHours(); - }; - - self.getMinutes = function() { - return self.date.getMinutes(); - }; - - self.getSeconds = function() { - return self.date.getSeconds(); - }; - - self.getMilliseconds = function() { - return self.date.getMilliseconds(); - }; - - self.getTimezoneOffset = function() { - return offset * 60; - }; - - self.getUTCFullYear = function() { - return self.origDate.getUTCFullYear(); - }; - - self.getUTCMonth = function() { - return self.origDate.getUTCMonth(); - }; - - self.getUTCDate = function() { - return self.origDate.getUTCDate(); - }; - - self.getUTCHours = function() { - return self.origDate.getUTCHours(); - }; - - self.getUTCMinutes = function() { - return self.origDate.getUTCMinutes(); - }; - - self.getUTCSeconds = function() { - return self.origDate.getUTCSeconds(); - }; - - self.getUTCMilliseconds = function() { - return self.origDate.getUTCMilliseconds(); - }; - - self.getDay = function() { - return self.date.getDay(); - }; - - // provide this method only on browsers that already have it - if (self.toISOString) { - self.toISOString = function() { - return padNumber(self.origDate.getUTCFullYear(), 4) + '-' + - padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' + - padNumber(self.origDate.getUTCDate(), 2) + 'T' + - padNumber(self.origDate.getUTCHours(), 2) + ':' + - padNumber(self.origDate.getUTCMinutes(), 2) + ':' + - padNumber(self.origDate.getUTCSeconds(), 2) + '.' + - padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z' - } - } - - //hide all methods not implemented in this mock that the Date prototype exposes - var unimplementedMethods = ['getUTCDay', - 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', - 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', - 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', - 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString', - 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; - - angular.forEach(unimplementedMethods, function(methodName) { - self[methodName] = function() { - throw Error("Method '" + methodName + "' is not implemented in the TzDate mock"); - }; - }); - - return self; - }; - - //make "tzDateInstance instanceof Date" return true - angular.mock.TzDate.prototype = Date.prototype; -})(); - -/** - * @ngdoc function - * @name angular.mock.createMockWindow - * @description - * - * This function creates a mock window object useful for controlling access ot setTimeout, but mocking out - * sufficient window's properties to allow Angular to execute. - * - * @example - * - *
-    beforeEach(module(function($provide) {
-      $provide.value('$window', window = angular.mock.createMockWindow());
-    }));
-
-    it('should do something', inject(function($window) {
-      var val = null;
-      $window.setTimeout(function() { val = 123; }, 10);
-      expect(val).toEqual(null);
-      window.setTimeout.expect(10).process();
-      expect(val).toEqual(123);
-    });
- * 
- * - */ -angular.mock.createMockWindow = function() { - var mockWindow = {}; - var setTimeoutQueue = []; - - mockWindow.document = window.document; - mockWindow.getComputedStyle = angular.bind(window, window.getComputedStyle); - mockWindow.scrollTo = angular.bind(window, window.scrollTo); - mockWindow.navigator = window.navigator; - mockWindow.setTimeout = function(fn, delay) { - setTimeoutQueue.push({fn: fn, delay: delay}); - }; - mockWindow.setTimeout.queue = setTimeoutQueue; - mockWindow.setTimeout.expect = function(delay) { - if (setTimeoutQueue.length > 0) { - return { - process: function() { - var tick = setTimeoutQueue.shift(); - expect(tick.delay).toEqual(delay); - tick.fn(); - } - }; - } else { - expect('SetTimoutQueue empty. Expecting delay of ').toEqual(delay); - } - }; - - return mockWindow; -}; - -/** - * @ngdoc function - * @name angular.mock.dump - * @description - * - * *NOTE*: this is not an injectable instance, just a globally available function. - * - * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for debugging. - * - * This method is also available on window, where it can be used to display objects on debug console. - * - * @param {*} object - any object to turn into string. - * @return {string} a serialized string of the argument - */ -angular.mock.dump = function(object) { - return serialize(object); - - function serialize(object) { - var out; - - if (angular.isElement(object)) { - object = angular.element(object); - out = angular.element('
'); - angular.forEach(object, function(element) { - out.append(angular.element(element).clone()); - }); - out = out.html(); - } else if (angular.isArray(object)) { - out = []; - angular.forEach(object, function(o) { - out.push(serialize(o)); - }); - out = '[ ' + out.join(', ') + ' ]'; - } else if (angular.isObject(object)) { - if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) { - out = serializeScope(object); - } else if (object instanceof Error) { - out = object.stack || ('' + object.name + ': ' + object.message); - } else { - out = angular.toJson(object, true); - } - } else { - out = String(object); - } - - return out; - } - - function serializeScope(scope, offset) { - offset = offset || ' '; - var log = [offset + 'Scope(' + scope.$id + '): {']; - for ( var key in scope ) { - if (scope.hasOwnProperty(key) && !key.match(/^(\$|this)/)) { - log.push(' ' + key + ': ' + angular.toJson(scope[key])); - } - } - var child = scope.$$childHead; - while(child) { - log.push(serializeScope(child, offset + ' ')); - child = child.$$nextSibling; - } - log.push('}'); - return log.join('\n' + offset); - } -}; - -/** - * @ngdoc object - * @name ngMock.$httpBackend - * @description - * Fake HTTP backend implementation suitable for unit testing applications that use the - * {@link ng.$http $http service}. - * - * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less - * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}. - * - * During unit testing, we want our unit tests to run quickly and have no external dependencies so - * we don’t want to send {@link https://developer.mozilla.org/en/xmlhttprequest XHR} or - * {@link http://en.wikipedia.org/wiki/JSONP JSONP} requests to a real server. All we really need is - * to verify whether a certain request has been sent or not, or alternatively just let the - * application make requests, respond with pre-trained responses and assert that the end result is - * what we expect it to be. - * - * This mock implementation can be used to respond with static or dynamic responses via the - * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc). - * - * When an Angular application needs some data from a server, it calls the $http service, which - * sends the request to a real server using $httpBackend service. With dependency injection, it is - * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify - * the requests and respond with some testing data without sending a request to real server. - * - * There are two ways to specify what test data should be returned as http responses by the mock - * backend when the code under test makes http requests: - * - * - `$httpBackend.expect` - specifies a request expectation - * - `$httpBackend.when` - specifies a backend definition - * - * - * # Request Expectations vs Backend Definitions - * - * Request expectations provide a way to make assertions about requests made by the application and - * to define responses for those requests. The test will fail if the expected requests are not made - * or they are made in the wrong order. - * - * Backend definitions allow you to define a fake backend for your application which doesn't assert - * if a particular request was made or not, it just returns a trained response if a request is made. - * The test will pass whether or not the request gets made during testing. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Request expectationsBackend definitions
Syntax.expect(...).respond(...).when(...).respond(...)
Typical usagestrict unit testsloose (black-box) unit testing
Fulfills multiple requestsNOYES
Order of requests mattersYESNO
Request requiredYESNO
Response requiredoptional (see below)YES
- * - * In cases where both backend definitions and request expectations are specified during unit - * testing, the request expectations are evaluated first. - * - * If a request expectation has no response specified, the algorithm will search your backend - * definitions for an appropriate response. - * - * If a request didn't match any expectation or if the expectation doesn't have the response - * defined, the backend definitions are evaluated in sequential order to see if any of them match - * the request. The response from the first matched definition is returned. - * - * - * # Flushing HTTP requests - * - * The $httpBackend used in production, always responds to requests with responses asynchronously. - * If we preserved this behavior in unit testing, we'd have to create async unit tests, which are - * hard to write, follow and maintain. At the same time the testing mock, can't respond - * synchronously because that would change the execution of the code under test. For this reason the - * mock $httpBackend has a `flush()` method, which allows the test to explicitly flush pending - * requests and thus preserving the async api of the backend, while allowing the test to execute - * synchronously. - * - * - * # Unit testing with mock $httpBackend - * - *
-   // controller
-   function MyController($scope, $http) {
-     $http.get('/auth.py').success(function(data) {
-       $scope.user = data;
-     });
-
-     this.saveMessage = function(message) {
-       $scope.status = 'Saving...';
-       $http.post('/add-msg.py', message).success(function(response) {
-         $scope.status = '';
-       }).error(function() {
-         $scope.status = 'ERROR!';
-       });
-     };
-   }
-
-   // testing controller
-   var $httpBackend;
-
-   beforeEach(inject(function($injector) {
-     $httpBackend = $injector.get('$httpBackend');
-
-     // backend definition common for all tests
-     $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
-   }));
-
-
-   afterEach(function() {
-     $httpBackend.verifyNoOutstandingExpectation();
-     $httpBackend.verifyNoOutstandingRequest();
-   });
-
-
-   it('should fetch authentication token', function() {
-     $httpBackend.expectGET('/auth.py');
-     var controller = scope.$new(MyController);
-     $httpBackend.flush();
-   });
-
-
-   it('should send msg to server', function() {
-     // now you don’t care about the authentication, but
-     // the controller will still send the request and
-     // $httpBackend will respond without you having to
-     // specify the expectation and response for this request
-     $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
-
-     var controller = scope.$new(MyController);
-     $httpBackend.flush();
-     controller.saveMessage('message content');
-     expect(controller.status).toBe('Saving...');
-     $httpBackend.flush();
-     expect(controller.status).toBe('');
-   });
-
-
-   it('should send auth header', function() {
-     $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
-       // check if the header was send, if it wasn't the expectation won't
-       // match the request and the test will fail
-       return headers['Authorization'] == 'xxx';
-     }).respond(201, '');
-
-     var controller = scope.$new(MyController);
-     controller.saveMessage('whatever');
-     $httpBackend.flush();
-   });
-   
- */ -angular.mock.$HttpBackendProvider = function() { - this.$get = ['$rootScope', createHttpBackendMock]; -}; - -/** - * General factory function for $httpBackend mock. - * Returns instance for unit testing (when no arguments specified): - * - passing through is disabled - * - auto flushing is disabled - * - * Returns instance for e2e testing (when `$delegate` and `$browser` specified): - * - passing through (delegating request to real backend) is enabled - * - auto flushing is enabled - * - * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified) - * @param {Object=} $browser Auto-flushing enabled if specified - * @return {Object} Instance of $httpBackend mock - */ -function createHttpBackendMock($rootScope, $delegate, $browser) { - var definitions = [], - expectations = [], - responses = [], - responsesPush = angular.bind(responses, responses.push); - - function createResponse(status, data, headers) { - if (angular.isFunction(status)) return status; - - return function() { - return angular.isNumber(status) - ? [status, data, headers] - : [200, status, data]; - }; - } - - // TODO(vojta): change params to: method, url, data, headers, callback - function $httpBackend(method, url, data, callback, headers, timeout) { - var xhr = new MockXhr(), - expectation = expectations[0], - wasExpected = false; - - function prettyPrint(data) { - return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp) - ? data - : angular.toJson(data); - } - - function wrapResponse(wrapped) { - if (!$browser && timeout && timeout.then) timeout.then(handleTimeout); - - return handleResponse; - - function handleResponse() { - var response = wrapped.response(method, url, data, headers); - xhr.$$respHeaders = response[2]; - callback(response[0], response[1], xhr.getAllResponseHeaders()); - } - - function handleTimeout() { - for (var i = 0, ii = responses.length; i < ii; i++) { - if (responses[i] === handleResponse) { - responses.splice(i, 1); - callback(-1, undefined, ''); - break; - } - } - } - } - - if (expectation && expectation.match(method, url)) { - if (!expectation.matchData(data)) - throw Error('Expected ' + expectation + ' with different data\n' + - 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data); - - if (!expectation.matchHeaders(headers)) - throw Error('Expected ' + expectation + ' with different headers\n' + - 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + - prettyPrint(headers)); - - expectations.shift(); - - if (expectation.response) { - responses.push(wrapResponse(expectation)); - return; - } - wasExpected = true; - } - - var i = -1, definition; - while ((definition = definitions[++i])) { - if (definition.match(method, url, data, headers || {})) { - if (definition.response) { - // if $browser specified, we do auto flush all requests - ($browser ? $browser.defer : responsesPush)(wrapResponse(definition)); - } else if (definition.passThrough) { - $delegate(method, url, data, callback, headers, timeout); - } else throw Error('No response defined !'); - return; - } - } - throw wasExpected ? - Error('No response defined !') : - Error('Unexpected request: ' + method + ' ' + url + '\n' + - (expectation ? 'Expected ' + expectation : 'No more request expected')); - } - - /** - * @ngdoc method - * @name ngMock.$httpBackend#when - * @methodOf ngMock.$httpBackend - * @description - * Creates a new backend definition. - * - * @param {string} method HTTP method. - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header - * object and returns true if the headers match the current definition. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - * - * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` - * – The respond method takes a set of static data to be returned or a function that can return - * an array containing response status (number), response data (string) and response headers - * (Object). - */ - $httpBackend.when = function(method, url, data, headers) { - var definition = new MockHttpExpectation(method, url, data, headers), - chain = { - respond: function(status, data, headers) { - definition.response = createResponse(status, data, headers); - } - }; - - if ($browser) { - chain.passThrough = function() { - definition.passThrough = true; - }; - } - - definitions.push(definition); - return chain; - }; - - /** - * @ngdoc method - * @name ngMock.$httpBackend#whenGET - * @methodOf ngMock.$httpBackend - * @description - * Creates a new backend definition for GET requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#whenHEAD - * @methodOf ngMock.$httpBackend - * @description - * Creates a new backend definition for HEAD requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#whenDELETE - * @methodOf ngMock.$httpBackend - * @description - * Creates a new backend definition for DELETE requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#whenPOST - * @methodOf ngMock.$httpBackend - * @description - * Creates a new backend definition for POST requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#whenPUT - * @methodOf ngMock.$httpBackend - * @description - * Creates a new backend definition for PUT requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#whenJSONP - * @methodOf ngMock.$httpBackend - * @description - * Creates a new backend definition for JSONP requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - createShortMethods('when'); - - - /** - * @ngdoc method - * @name ngMock.$httpBackend#expect - * @methodOf ngMock.$httpBackend - * @description - * Creates a new request expectation. - * - * @param {string} method HTTP method. - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header - * object and returns true if the headers match the current expectation. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - * - * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` - * – The respond method takes a set of static data to be returned or a function that can return - * an array containing response status (number), response data (string) and response headers - * (Object). - */ - $httpBackend.expect = function(method, url, data, headers) { - var expectation = new MockHttpExpectation(method, url, data, headers); - expectations.push(expectation); - return { - respond: function(status, data, headers) { - expectation.response = createResponse(status, data, headers); - } - }; - }; - - - /** - * @ngdoc method - * @name ngMock.$httpBackend#expectGET - * @methodOf ngMock.$httpBackend - * @description - * Creates a new request expectation for GET requests. For more info see `expect()`. - * - * @param {string|RegExp} url HTTP url. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. See #expect for more info. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#expectHEAD - * @methodOf ngMock.$httpBackend - * @description - * Creates a new request expectation for HEAD requests. For more info see `expect()`. - * - * @param {string|RegExp} url HTTP url. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#expectDELETE - * @methodOf ngMock.$httpBackend - * @description - * Creates a new request expectation for DELETE requests. For more info see `expect()`. - * - * @param {string|RegExp} url HTTP url. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#expectPOST - * @methodOf ngMock.$httpBackend - * @description - * Creates a new request expectation for POST requests. For more info see `expect()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#expectPUT - * @methodOf ngMock.$httpBackend - * @description - * Creates a new request expectation for PUT requests. For more info see `expect()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#expectPATCH - * @methodOf ngMock.$httpBackend - * @description - * Creates a new request expectation for PATCH requests. For more info see `expect()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - - /** - * @ngdoc method - * @name ngMock.$httpBackend#expectJSONP - * @methodOf ngMock.$httpBackend - * @description - * Creates a new request expectation for JSONP requests. For more info see `expect()`. - * - * @param {string|RegExp} url HTTP url. - * @returns {requestHandler} Returns an object with `respond` method that control how a matched - * request is handled. - */ - createShortMethods('expect'); - - - /** - * @ngdoc method - * @name ngMock.$httpBackend#flush - * @methodOf ngMock.$httpBackend - * @description - * Flushes all pending requests using the trained responses. - * - * @param {number=} count Number of responses to flush (in the order they arrived). If undefined, - * all pending requests will be flushed. If there are no pending requests when the flush method - * is called an exception is thrown (as this typically a sign of programming error). - */ - $httpBackend.flush = function(count) { - $rootScope.$digest(); - if (!responses.length) throw Error('No pending request to flush !'); - - if (angular.isDefined(count)) { - while (count--) { - if (!responses.length) throw Error('No more pending request to flush !'); - responses.shift()(); - } - } else { - while (responses.length) { - responses.shift()(); - } - } - $httpBackend.verifyNoOutstandingExpectation(); - }; - - - /** - * @ngdoc method - * @name ngMock.$httpBackend#verifyNoOutstandingExpectation - * @methodOf ngMock.$httpBackend - * @description - * Verifies that all of the requests defined via the `expect` api were made. If any of the - * requests were not made, verifyNoOutstandingExpectation throws an exception. - * - * Typically, you would call this method following each test case that asserts requests using an - * "afterEach" clause. - * - *
-   *   afterEach($httpBackend.verifyExpectations);
-   * 
- */ - $httpBackend.verifyNoOutstandingExpectation = function() { - $rootScope.$digest(); - if (expectations.length) { - throw Error('Unsatisfied requests: ' + expectations.join(', ')); - } - }; - - - /** - * @ngdoc method - * @name ngMock.$httpBackend#verifyNoOutstandingRequest - * @methodOf ngMock.$httpBackend - * @description - * Verifies that there are no outstanding requests that need to be flushed. - * - * Typically, you would call this method following each test case that asserts requests using an - * "afterEach" clause. - * - *
-   *   afterEach($httpBackend.verifyNoOutstandingRequest);
-   * 
- */ - $httpBackend.verifyNoOutstandingRequest = function() { - if (responses.length) { - throw Error('Unflushed requests: ' + responses.length); - } - }; - - - /** - * @ngdoc method - * @name ngMock.$httpBackend#resetExpectations - * @methodOf ngMock.$httpBackend - * @description - * Resets all request expectations, but preserves all backend definitions. Typically, you would - * call resetExpectations during a multiple-phase test when you want to reuse the same instance of - * $httpBackend mock. - */ - $httpBackend.resetExpectations = function() { - expectations.length = 0; - responses.length = 0; - }; - - return $httpBackend; - - - function createShortMethods(prefix) { - angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) { - $httpBackend[prefix + method] = function(url, headers) { - return $httpBackend[prefix](method, url, undefined, headers) - } - }); - - angular.forEach(['PUT', 'POST', 'PATCH'], function(method) { - $httpBackend[prefix + method] = function(url, data, headers) { - return $httpBackend[prefix](method, url, data, headers) - } - }); - } -} - -function MockHttpExpectation(method, url, data, headers) { - - this.data = data; - this.headers = headers; - - this.match = function(m, u, d, h) { - if (method != m) return false; - if (!this.matchUrl(u)) return false; - if (angular.isDefined(d) && !this.matchData(d)) return false; - if (angular.isDefined(h) && !this.matchHeaders(h)) return false; - return true; - }; - - this.matchUrl = function(u) { - if (!url) return true; - if (angular.isFunction(url.test)) return url.test(u); - return url == u; - }; - - this.matchHeaders = function(h) { - if (angular.isUndefined(headers)) return true; - if (angular.isFunction(headers)) return headers(h); - return angular.equals(headers, h); - }; - - this.matchData = function(d) { - if (angular.isUndefined(data)) return true; - if (data && angular.isFunction(data.test)) return data.test(d); - if (data && !angular.isString(data)) return angular.toJson(data) == d; - return data == d; - }; - - this.toString = function() { - return method + ' ' + url; - }; -} - -function MockXhr() { - - // hack for testing $http, $httpBackend - MockXhr.$$lastInstance = this; - - this.open = function(method, url, async) { - this.$$method = method; - this.$$url = url; - this.$$async = async; - this.$$reqHeaders = {}; - this.$$respHeaders = {}; - }; - - this.send = function(data) { - this.$$data = data; - }; - - this.setRequestHeader = function(key, value) { - this.$$reqHeaders[key] = value; - }; - - this.getResponseHeader = function(name) { - // the lookup must be case insensitive, that's why we try two quick lookups and full scan at last - var header = this.$$respHeaders[name]; - if (header) return header; - - name = angular.lowercase(name); - header = this.$$respHeaders[name]; - if (header) return header; - - header = undefined; - angular.forEach(this.$$respHeaders, function(headerVal, headerName) { - if (!header && angular.lowercase(headerName) == name) header = headerVal; - }); - return header; - }; - - this.getAllResponseHeaders = function() { - var lines = []; - - angular.forEach(this.$$respHeaders, function(value, key) { - lines.push(key + ': ' + value); - }); - return lines.join('\n'); - }; - - this.abort = angular.noop; -} - - -/** - * @ngdoc function - * @name ngMock.$timeout - * @description - * - * This service is just a simple decorator for {@link ng.$timeout $timeout} service - * that adds a "flush" and "verifyNoPendingTasks" methods. - */ - -angular.mock.$TimeoutDecorator = function($delegate, $browser) { - - /** - * @ngdoc method - * @name ngMock.$timeout#flush - * @methodOf ngMock.$timeout - * @description - * - * Flushes the queue of pending tasks. - */ - $delegate.flush = function() { - $browser.defer.flush(); - }; - - /** - * @ngdoc method - * @name ngMock.$timeout#verifyNoPendingTasks - * @methodOf ngMock.$timeout - * @description - * - * Verifies that there are no pending tasks that need to be flushed. - */ - $delegate.verifyNoPendingTasks = function() { - if ($browser.deferredFns.length) { - throw Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' + - formatPendingTasksAsString($browser.deferredFns)); - } - }; - - function formatPendingTasksAsString(tasks) { - var result = []; - angular.forEach(tasks, function(task) { - result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}'); - }); - - return result.join(', '); - } - - return $delegate; -}; - -/** - * - */ -angular.mock.$RootElementProvider = function() { - this.$get = function() { - return angular.element('
'); - } -}; - -/** - * @ngdoc overview - * @name ngMock - * @description - * - * The `ngMock` is an angular module which is used with `ng` module and adds unit-test configuration as well as useful - * mocks to the {@link AUTO.$injector $injector}. - */ -angular.module('ngMock', ['ng']).provider({ - $browser: angular.mock.$BrowserProvider, - $exceptionHandler: angular.mock.$ExceptionHandlerProvider, - $log: angular.mock.$LogProvider, - $httpBackend: angular.mock.$HttpBackendProvider, - $rootElement: angular.mock.$RootElementProvider -}).config(function($provide) { - $provide.decorator('$timeout', angular.mock.$TimeoutDecorator); -}); - -/** - * @ngdoc overview - * @name ngMockE2E - * @description - * - * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing. - * Currently there is only one mock present in this module - - * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock. - */ -angular.module('ngMockE2E', ['ng']).config(function($provide) { - $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); -}); - -/** - * @ngdoc object - * @name ngMockE2E.$httpBackend - * @description - * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of - * applications that use the {@link ng.$http $http service}. - * - * *Note*: For fake http backend implementation suitable for unit testing please see - * {@link ngMock.$httpBackend unit-testing $httpBackend mock}. - * - * This implementation can be used to respond with static or dynamic responses via the `when` api - * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the - * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch - * templates from a webserver). - * - * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application - * is being developed with the real backend api replaced with a mock, it is often desirable for - * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch - * templates or static files from the webserver). To configure the backend with this behavior - * use the `passThrough` request handler of `when` instead of `respond`. - * - * Additionally, we don't want to manually have to flush mocked out requests like we do during unit - * testing. For this reason the e2e $httpBackend automatically flushes mocked out requests - * automatically, closely simulating the behavior of the XMLHttpRequest object. - * - * To setup the application to run with this http backend, you have to create a module that depends - * on the `ngMockE2E` and your application modules and defines the fake backend: - * - *
- *   myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
- *   myAppDev.run(function($httpBackend) {
- *     phones = [{name: 'phone1'}, {name: 'phone2'}];
- *
- *     // returns the current list of phones
- *     $httpBackend.whenGET('/phones').respond(phones);
- *
- *     // adds a new phone to the phones array
- *     $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
- *       phones.push(angular.fromJSON(data));
- *     });
- *     $httpBackend.whenGET(/^\/templates\//).passThrough();
- *     //...
- *   });
- * 
- * - * Afterwards, bootstrap your app with this new module. - */ - -/** - * @ngdoc method - * @name ngMockE2E.$httpBackend#when - * @methodOf ngMockE2E.$httpBackend - * @description - * Creates a new backend definition. - * - * @param {string} method HTTP method. - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header - * object and returns true if the headers match the current definition. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. - * - * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` - * – The respond method takes a set of static data to be returned or a function that can return - * an array containing response status (number), response data (string) and response headers - * (Object). - * - passThrough – `{function()}` – Any request matching a backend definition with `passThrough` - * handler, will be pass through to the real backend (an XHR request will be made to the - * server. - */ - -/** - * @ngdoc method - * @name ngMockE2E.$httpBackend#whenGET - * @methodOf ngMockE2E.$httpBackend - * @description - * Creates a new backend definition for GET requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. - */ - -/** - * @ngdoc method - * @name ngMockE2E.$httpBackend#whenHEAD - * @methodOf ngMockE2E.$httpBackend - * @description - * Creates a new backend definition for HEAD requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. - */ - -/** - * @ngdoc method - * @name ngMockE2E.$httpBackend#whenDELETE - * @methodOf ngMockE2E.$httpBackend - * @description - * Creates a new backend definition for DELETE requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. - */ - -/** - * @ngdoc method - * @name ngMockE2E.$httpBackend#whenPOST - * @methodOf ngMockE2E.$httpBackend - * @description - * Creates a new backend definition for POST requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. - */ - -/** - * @ngdoc method - * @name ngMockE2E.$httpBackend#whenPUT - * @methodOf ngMockE2E.$httpBackend - * @description - * Creates a new backend definition for PUT requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. - */ - -/** - * @ngdoc method - * @name ngMockE2E.$httpBackend#whenPATCH - * @methodOf ngMockE2E.$httpBackend - * @description - * Creates a new backend definition for PATCH requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. - */ - -/** - * @ngdoc method - * @name ngMockE2E.$httpBackend#whenJSONP - * @methodOf ngMockE2E.$httpBackend - * @description - * Creates a new backend definition for JSONP requests. For more info see `when()`. - * - * @param {string|RegExp} url HTTP url. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. - */ -angular.mock.e2e = {}; -angular.mock.e2e.$httpBackendDecorator = ['$rootScope', '$delegate', '$browser', createHttpBackendMock]; - - -angular.mock.clearDataCache = function() { - var key, - cache = angular.element.cache; - - for(key in cache) { - if (cache.hasOwnProperty(key)) { - var handle = cache[key].handle; - - handle && angular.element(handle.elem).unbind(); - delete cache[key]; - } - } -}; - - -window.jstestdriver && (function(window) { - /** - * Global method to output any number of objects into JSTD console. Useful for debugging. - */ - window.dump = function() { - var args = []; - angular.forEach(arguments, function(arg) { - args.push(angular.mock.dump(arg)); - }); - jstestdriver.console.log.apply(jstestdriver.console, args); - if (window.console) { - window.console.log.apply(window.console, args); - } - }; -})(window); - - -(window.jasmine || window.mocha) && (function(window) { - - var currentSpec = null; - - beforeEach(function() { - currentSpec = this; - }); - - afterEach(function() { - var injector = currentSpec.$injector; - - currentSpec.$injector = null; - currentSpec.$modules = null; - currentSpec = null; - - if (injector) { - injector.get('$rootElement').unbind(); - injector.get('$browser').pollFns.length = 0; - } - - angular.mock.clearDataCache(); - - // clean up jquery's fragment cache - angular.forEach(angular.element.fragments, function(val, key) { - delete angular.element.fragments[key]; - }); - - MockXhr.$$lastInstance = null; - - angular.forEach(angular.callbacks, function(val, key) { - delete angular.callbacks[key]; - }); - angular.callbacks.counter = 0; - }); - - function isSpecRunning() { - return currentSpec && (window.mocha || currentSpec.queue.running); - } - - /** - * @ngdoc function - * @name angular.mock.module - * @description - * - * *NOTE*: This function is also published on window for easy access.
- * - * This function registers a module configuration code. It collects the configuration information - * which will be used when the injector is created by {@link angular.mock.inject inject}. - * - * See {@link angular.mock.inject inject} for usage example - * - * @param {...(string|Function)} fns any number of modules which are represented as string - * aliases or as anonymous module initialization functions. The modules are used to - * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. - */ - window.module = angular.mock.module = function() { - var moduleFns = Array.prototype.slice.call(arguments, 0); - return isSpecRunning() ? workFn() : workFn; - ///////////////////// - function workFn() { - if (currentSpec.$injector) { - throw Error('Injector already created, can not register a module!'); - } else { - var modules = currentSpec.$modules || (currentSpec.$modules = []); - angular.forEach(moduleFns, function(module) { - modules.push(module); - }); - } - } - }; - - /** - * @ngdoc function - * @name angular.mock.inject - * @description - * - * *NOTE*: This function is also published on window for easy access.
- * - * The inject function wraps a function into an injectable function. The inject() creates new - * instance of {@link AUTO.$injector $injector} per test, which is then used for - * resolving references. - * - * See also {@link angular.mock.module module} - * - * Example of what a typical jasmine tests looks like with the inject method. - *
-   *
-   *   angular.module('myApplicationModule', [])
-   *       .value('mode', 'app')
-   *       .value('version', 'v1.0.1');
-   *
-   *
-   *   describe('MyApp', function() {
-   *
-   *     // You need to load modules that you want to test,
-   *     // it loads only the "ng" module by default.
-   *     beforeEach(module('myApplicationModule'));
-   *
-   *
-   *     // inject() is used to inject arguments of all given functions
-   *     it('should provide a version', inject(function(mode, version) {
-   *       expect(version).toEqual('v1.0.1');
-   *       expect(mode).toEqual('app');
-   *     }));
-   *
-   *
-   *     // The inject and module method can also be used inside of the it or beforeEach
-   *     it('should override a version and test the new version is injected', function() {
-   *       // module() takes functions or strings (module aliases)
-   *       module(function($provide) {
-   *         $provide.value('version', 'overridden'); // override version here
-   *       });
-   *
-   *       inject(function(version) {
-   *         expect(version).toEqual('overridden');
-   *       });
-   *     ));
-   *   });
-   *
-   * 
- * - * @param {...Function} fns any number of functions which will be injected using the injector. - */ - window.inject = angular.mock.inject = function() { - var blockFns = Array.prototype.slice.call(arguments, 0); - var errorForStack = new Error('Declaration Location'); - return isSpecRunning() ? workFn() : workFn; - ///////////////////// - function workFn() { - var modules = currentSpec.$modules || []; - - modules.unshift('ngMock'); - modules.unshift('ng'); - var injector = currentSpec.$injector; - if (!injector) { - injector = currentSpec.$injector = angular.injector(modules); - } - for(var i = 0, ii = blockFns.length; i < ii; i++) { - try { - injector.invoke(blockFns[i] || angular.noop, this); - } catch (e) { - if(e.stack && errorForStack) e.stack += '\n' + errorForStack.stack; - throw e; - } finally { - errorForStack = null; - } - } - } - }; -})(window); diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-resource.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-resource.js deleted file mode 100644 index acaa84cd52..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-resource.js +++ /dev/null @@ -1,537 +0,0 @@ -/** - * @license AngularJS v1.1.5 - * (c) 2010-2012 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, angular, undefined) { -'use strict'; - -/** - * @ngdoc overview - * @name ngResource - * @description - */ - -/** - * @ngdoc object - * @name ngResource.$resource - * @requires $http - * - * @description - * A factory which creates a resource object that lets you interact with - * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources. - * - * The returned resource object has action methods which provide high-level behaviors without - * the need to interact with the low level {@link ng.$http $http} service. - * - * # Installation - * To use $resource make sure you have included the `angular-resource.js` that comes in Angular - * package. You can also find this file on Google CDN, bower as well as at - * {@link http://code.angularjs.org/ code.angularjs.org}. - * - * Finally load the module in your application: - * - * angular.module('app', ['ngResource']); - * - * and you are ready to get started! - * - * @param {string} url A parametrized URL template with parameters prefixed by `:` as in - * `/user/:username`. If you are using a URL with a port number (e.g. - * `http://example.com:8080/api`), you'll need to escape the colon character before the port - * number, like this: `$resource('http://example.com\\:8080/api')`. - * - * If you are using a url with a suffix, just add the suffix, like this: - * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json') - * or even `$resource('http://example.com/resource/:resource_id.:format')` - * If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be - * collapsed down to a single `.`. If you need this sequence to appear and not collapse then you - * can escape it with `/\.`. - * - * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in - * `actions` methods. If any of the parameter value is a function, it will be executed every time - * when a param value needs to be obtained for a request (unless the param was overridden). - * - * Each key value in the parameter object is first bound to url template if present and then any - * excess keys are appended to the url search query after the `?`. - * - * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in - * URL `/path/greet?salutation=Hello`. - * - * If the parameter value is prefixed with `@` then the value of that parameter is extracted from - * the data object (useful for non-GET operations). - * - * @param {Object.=} actions Hash with declaration of custom action that should extend the - * default set of resource actions. The declaration should be created in the format of {@link - * ng.$http#Parameters $http.config}: - * - * {action1: {method:?, params:?, isArray:?, headers:?, ...}, - * action2: {method:?, params:?, isArray:?, headers:?, ...}, - * ...} - * - * Where: - * - * - **`action`** – {string} – The name of action. This name becomes the name of the method on your - * resource object. - * - **`method`** – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`, - * and `JSONP`. - * - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of the - * parameter value is a function, it will be executed every time when a param value needs to be - * obtained for a request (unless the param was overridden). - * - **`url`** – {string} – action specific `url` override. The url templating is supported just like - * for the resource-level urls. - * - **`isArray`** – {boolean=} – If true then the returned object for this action is an array, see - * `returns` section. - * - **`transformRequest`** – `{function(data, headersGetter)|Array.}` – - * transform function or an array of such functions. The transform function takes the http - * request body and headers and returns its transformed (typically serialized) version. - * - **`transformResponse`** – `{function(data, headersGetter)|Array.}` – - * transform function or an array of such functions. The transform function takes the http - * response body and headers and returns its transformed (typically deserialized) version. - * - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the - * GET request, otherwise if a cache instance built with - * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for - * caching. - * - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that - * should abort the request when resolved. - * - **`withCredentials`** - `{boolean}` - whether to to set the `withCredentials` flag on the - * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5 - * requests with credentials} for more information. - * - **`responseType`** - `{string}` - see {@link - * https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}. - * - * @returns {Object} A resource "class" object with methods for the default set of resource actions - * optionally extended with custom `actions`. The default set contains these actions: - * - * { 'get': {method:'GET'}, - * 'save': {method:'POST'}, - * 'query': {method:'GET', isArray:true}, - * 'remove': {method:'DELETE'}, - * 'delete': {method:'DELETE'} }; - * - * Calling these methods invoke an {@link ng.$http} with the specified http method, - * destination and parameters. When the data is returned from the server then the object is an - * instance of the resource class. The actions `save`, `remove` and `delete` are available on it - * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create, - * read, update, delete) on server-side data like this: - *
-        var User = $resource('/user/:userId', {userId:'@id'});
-        var user = User.get({userId:123}, function() {
-          user.abc = true;
-          user.$save();
-        });
-     
- * - * It is important to realize that invoking a $resource object method immediately returns an - * empty reference (object or array depending on `isArray`). Once the data is returned from the - * server the existing reference is populated with the actual data. This is a useful trick since - * usually the resource is assigned to a model which is then rendered by the view. Having an empty - * object results in no rendering, once the data arrives from the server then the object is - * populated with the data and the view automatically re-renders itself showing the new data. This - * means that in most case one never has to write a callback function for the action methods. - * - * The action methods on the class object or instance object can be invoked with the following - * parameters: - * - * - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])` - * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])` - * - non-GET instance actions: `instance.$action([parameters], [success], [error])` - * - * - * The Resource instances and collection have these additional properties: - * - * - `$then`: the `then` method of a {@link ng.$q promise} derived from the underlying - * {@link ng.$http $http} call. - * - * The success callback for the `$then` method will be resolved if the underlying `$http` requests - * succeeds. - * - * The success callback is called with a single object which is the {@link ng.$http http response} - * object extended with a new property `resource`. This `resource` property is a reference to the - * result of the resource action — resource object or array of resources. - * - * The error callback is called with the {@link ng.$http http response} object when an http - * error occurs. - * - * - `$resolved`: true if the promise has been resolved (either with success or rejection); - * Knowing if the Resource has been resolved is useful in data-binding. - * - * @example - * - * # Credit card resource - * - *
-     // Define CreditCard class
-     var CreditCard = $resource('/user/:userId/card/:cardId',
-      {userId:123, cardId:'@id'}, {
-       charge: {method:'POST', params:{charge:true}}
-      });
-
-     // We can retrieve a collection from the server
-     var cards = CreditCard.query(function() {
-       // GET: /user/123/card
-       // server returns: [ {id:456, number:'1234', name:'Smith'} ];
-
-       var card = cards[0];
-       // each item is an instance of CreditCard
-       expect(card instanceof CreditCard).toEqual(true);
-       card.name = "J. Smith";
-       // non GET methods are mapped onto the instances
-       card.$save();
-       // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
-       // server returns: {id:456, number:'1234', name: 'J. Smith'};
-
-       // our custom method is mapped as well.
-       card.$charge({amount:9.99});
-       // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
-     });
-
-     // we can create an instance as well
-     var newCard = new CreditCard({number:'0123'});
-     newCard.name = "Mike Smith";
-     newCard.$save();
-     // POST: /user/123/card {number:'0123', name:'Mike Smith'}
-     // server returns: {id:789, number:'01234', name: 'Mike Smith'};
-     expect(newCard.id).toEqual(789);
- * 
- * - * The object returned from this function execution is a resource "class" which has "static" method - * for each action in the definition. - * - * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and `headers`. - * When the data is returned from the server then the object is an instance of the resource type and - * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD - * operations (create, read, update, delete) on server-side data. - -
-     var User = $resource('/user/:userId', {userId:'@id'});
-     var user = User.get({userId:123}, function() {
-       user.abc = true;
-       user.$save();
-     });
-   
- * - * It's worth noting that the success callback for `get`, `query` and other method gets passed - * in the response that came from the server as well as $http header getter function, so one - * could rewrite the above example and get access to http headers as: - * -
-     var User = $resource('/user/:userId', {userId:'@id'});
-     User.get({userId:123}, function(u, getResponseHeaders){
-       u.abc = true;
-       u.$save(function(u, putResponseHeaders) {
-         //u => saved user object
-         //putResponseHeaders => $http header getter
-       });
-     });
-   
- - * # Buzz client - - Let's look at what a buzz client created with the `$resource` service looks like: - - - - -
- - -
-
-

- - {{item.actor.name}} - Expand replies: {{item.links.replies[0].count}} -

- {{item.object.content | html}} -
- - {{reply.actor.name}}: {{reply.content | html}} -
-
-
-
- - -
- */ -angular.module('ngResource', ['ng']). - factory('$resource', ['$http', '$parse', function($http, $parse) { - var DEFAULT_ACTIONS = { - 'get': {method:'GET'}, - 'save': {method:'POST'}, - 'query': {method:'GET', isArray:true}, - 'remove': {method:'DELETE'}, - 'delete': {method:'DELETE'} - }; - var noop = angular.noop, - forEach = angular.forEach, - extend = angular.extend, - copy = angular.copy, - isFunction = angular.isFunction, - getter = function(obj, path) { - return $parse(path)(obj); - }; - - /** - * We need our custom method because encodeURIComponent is too aggressive and doesn't follow - * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path - * segments: - * segment = *pchar - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * pct-encoded = "%" HEXDIG HEXDIG - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ - function encodeUriSegment(val) { - return encodeUriQuery(val, true). - replace(/%26/gi, '&'). - replace(/%3D/gi, '='). - replace(/%2B/gi, '+'); - } - - - /** - * This method is intended for encoding *key* or *value* parts of query component. We need a custom - * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be - * encoded per http://tools.ietf.org/html/rfc3986: - * query = *( pchar / "/" / "?" ) - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * pct-encoded = "%" HEXDIG HEXDIG - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ - function encodeUriQuery(val, pctEncodeSpaces) { - return encodeURIComponent(val). - replace(/%40/gi, '@'). - replace(/%3A/gi, ':'). - replace(/%24/g, '$'). - replace(/%2C/gi, ','). - replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); - } - - function Route(template, defaults) { - this.template = template; - this.defaults = defaults || {}; - this.urlParams = {}; - } - - Route.prototype = { - setUrlParams: function(config, params, actionUrl) { - var self = this, - url = actionUrl || self.template, - val, - encodedVal; - - var urlParams = self.urlParams = {}; - forEach(url.split(/\W/), function(param){ - if (param && (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) { - urlParams[param] = true; - } - }); - url = url.replace(/\\:/g, ':'); - - params = params || {}; - forEach(self.urlParams, function(_, urlParam){ - val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam]; - if (angular.isDefined(val) && val !== null) { - encodedVal = encodeUriSegment(val); - url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), encodedVal + "$1"); - } else { - url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match, - leadingSlashes, tail) { - if (tail.charAt(0) == '/') { - return tail; - } else { - return leadingSlashes + tail; - } - }); - } - }); - - // strip trailing slashes and set the url - url = url.replace(/\/+$/, ''); - // then replace collapse `/.` if found in the last URL path segment before the query - // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x` - url = url.replace(/\/\.(?=\w+($|\?))/, '.'); - // replace escaped `/\.` with `/.` - config.url = url.replace(/\/\\\./, '/.'); - - - // set params - delegate param encoding to $http - forEach(params, function(value, key){ - if (!self.urlParams[key]) { - config.params = config.params || {}; - config.params[key] = value; - } - }); - } - }; - - - function ResourceFactory(url, paramDefaults, actions) { - var route = new Route(url); - - actions = extend({}, DEFAULT_ACTIONS, actions); - - function extractParams(data, actionParams){ - var ids = {}; - actionParams = extend({}, paramDefaults, actionParams); - forEach(actionParams, function(value, key){ - if (isFunction(value)) { value = value(); } - ids[key] = value && value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value; - }); - return ids; - } - - function Resource(value){ - copy(value || {}, this); - } - - forEach(actions, function(action, name) { - action.method = angular.uppercase(action.method); - var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH'; - Resource[name] = function(a1, a2, a3, a4) { - var params = {}; - var data; - var success = noop; - var error = null; - var promise; - - switch(arguments.length) { - case 4: - error = a4; - success = a3; - //fallthrough - case 3: - case 2: - if (isFunction(a2)) { - if (isFunction(a1)) { - success = a1; - error = a2; - break; - } - - success = a2; - error = a3; - //fallthrough - } else { - params = a1; - data = a2; - success = a3; - break; - } - case 1: - if (isFunction(a1)) success = a1; - else if (hasBody) data = a1; - else params = a1; - break; - case 0: break; - default: - throw "Expected between 0-4 arguments [params, data, success, error], got " + - arguments.length + " arguments."; - } - - var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data)); - var httpConfig = {}, - promise; - - forEach(action, function(value, key) { - if (key != 'params' && key != 'isArray' ) { - httpConfig[key] = copy(value); - } - }); - httpConfig.data = data; - route.setUrlParams(httpConfig, extend({}, extractParams(data, action.params || {}), params), action.url); - - function markResolved() { value.$resolved = true; } - - promise = $http(httpConfig); - value.$resolved = false; - - promise.then(markResolved, markResolved); - value.$then = promise.then(function(response) { - var data = response.data; - var then = value.$then, resolved = value.$resolved; - - if (data) { - if (action.isArray) { - value.length = 0; - forEach(data, function(item) { - value.push(new Resource(item)); - }); - } else { - copy(data, value); - value.$then = then; - value.$resolved = resolved; - } - } - - (success||noop)(value, response.headers); - - response.resource = value; - return response; - }, error).then; - - return value; - }; - - - Resource.prototype['$' + name] = function(a1, a2, a3) { - var params = extractParams(this), - success = noop, - error; - - switch(arguments.length) { - case 3: params = a1; success = a2; error = a3; break; - case 2: - case 1: - if (isFunction(a1)) { - success = a1; - error = a2; - } else { - params = a1; - success = a2 || noop; - } - case 0: break; - default: - throw "Expected between 1-3 arguments [params, success, error], got " + - arguments.length + " arguments."; - } - var data = hasBody ? this : undefined; - Resource[name].call(this, params, data, success, error); - }; - }); - - Resource.bind = function(additionalParamDefaults){ - return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions); - }; - - return Resource; - } - - return ResourceFactory; - }]); - - -})(window, window.angular); diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-resource.min.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-resource.min.js deleted file mode 100644 index a8848fa0c2..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-resource.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - AngularJS v1.1.5 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(B,f,w){'use strict';f.module("ngResource",["ng"]).factory("$resource",["$http","$parse",function(x,y){function u(b,d){this.template=b;this.defaults=d||{};this.urlParams={}}function v(b,d,e){function j(c,b){var p={},b=m({},d,b);l(b,function(a,b){k(a)&&(a=a());var g;a&&a.charAt&&a.charAt(0)=="@"?(g=a.substr(1),g=y(g)(c)):g=a;p[b]=g});return p}function c(c){t(c||{},this)}var n=new u(b),e=m({},z,e);l(e,function(b,d){b.method=f.uppercase(b.method);var p=b.method=="POST"||b.method=="PUT"||b.method== -"PATCH";c[d]=function(a,d,g,A){function f(){h.$resolved=!0}var i={},e,o=q,r=null;switch(arguments.length){case 4:r=A,o=g;case 3:case 2:if(k(d)){if(k(a)){o=a;r=d;break}o=d;r=g}else{i=a;e=d;o=g;break}case 1:k(a)?o=a:p?e=a:i=a;break;case 0:break;default:throw"Expected between 0-4 arguments [params, data, success, error], got "+arguments.length+" arguments.";}var h=this instanceof c?this:b.isArray?[]:new c(e),s={};l(b,function(a,b){b!="params"&&b!="isArray"&&(s[b]=t(a))});s.data=e;n.setUrlParams(s,m({}, -j(e,b.params||{}),i),b.url);i=x(s);h.$resolved=!1;i.then(f,f);h.$then=i.then(function(a){var d=a.data,g=h.$then,e=h.$resolved;if(d)b.isArray?(h.length=0,l(d,function(a){h.push(new c(a))})):(t(d,h),h.$then=g,h.$resolved=e);(o||q)(h,a.headers);a.resource=h;return a},r).then;return h};c.prototype["$"+d]=function(a,b,g){var e=j(this),f=q,i;switch(arguments.length){case 3:e=a;f=b;i=g;break;case 2:case 1:k(a)?(f=a,i=b):(e=a,f=b||q);case 0:break;default:throw"Expected between 1-3 arguments [params, success, error], got "+ -arguments.length+" arguments.";}c[d].call(this,e,p?this:w,f,i)}});c.bind=function(c){return v(b,m({},d,c),e)};return c}var z={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},q=f.noop,l=f.forEach,m=f.extend,t=f.copy,k=f.isFunction;u.prototype={setUrlParams:function(b,d,e){var j=this,c=e||j.template,n,k,m=j.urlParams={};l(c.split(/\W/),function(b){b&&RegExp("(^|[^\\\\]):"+b+"(\\W|$)").test(c)&&(m[b]=!0)});c=c.replace(/\\:/g, -":");d=d||{};l(j.urlParams,function(b,a){n=d.hasOwnProperty(a)?d[a]:j.defaults[a];f.isDefined(n)&&n!==null?(k=encodeURIComponent(n).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),c=c.replace(RegExp(":"+a+"(\\W|$)","g"),k+"$1")):c=c.replace(RegExp("(/?):"+a+"(\\W|$)","g"),function(b,a,c){return c.charAt(0)=="/"?c:a+c})});c=c.replace(/\/+$/,"");c=c.replace(/\/\.(?=\w+($|\?))/,"."); -b.url=c.replace(/\/\\\./,"/.");l(d,function(c,a){if(!j.urlParams[a])b.params=b.params||{},b.params[a]=c})}};return v}])})(window,window.angular); diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-sanitize.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-sanitize.js deleted file mode 100644 index 8bb03a7ddb..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-sanitize.js +++ /dev/null @@ -1,558 +0,0 @@ -/** - * @license AngularJS v1.1.5 - * (c) 2010-2012 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, angular, undefined) { -'use strict'; - -/** - * @ngdoc overview - * @name ngSanitize - * @description - */ - -/* - * HTML Parser By Misko Hevery (misko@hevery.com) - * based on: HTML Parser By John Resig (ejohn.org) - * Original code by Erik Arvidsson, Mozilla Public License - * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js - * - * // Use like so: - * htmlParser(htmlString, { - * start: function(tag, attrs, unary) {}, - * end: function(tag) {}, - * chars: function(text) {}, - * comment: function(text) {} - * }); - * - */ - - -/** - * @ngdoc service - * @name ngSanitize.$sanitize - * @function - * - * @description - * The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are - * then serialized back to properly escaped html string. This means that no unsafe input can make - * it into the returned string, however, since our parser is more strict than a typical browser - * parser, it's possible that some obscure input, which would be recognized as valid HTML by a - * browser, won't make it through the sanitizer. - * - * @param {string} html Html input. - * @returns {string} Sanitized html. - * - * @example - - - -
- Snippet: - - - - - - - - - - - - - - - - - - - - - -
FilterSourceRendered
html filter -
<div ng-bind-html="snippet">
</div>
-
-
-
no filter
<div ng-bind="snippet">
</div>
unsafe html filter
<div ng-bind-html-unsafe="snippet">
</div>
-
-
- - it('should sanitize the html snippet ', function() { - expect(using('#html-filter').element('div').html()). - toBe('

an html\nclick here\nsnippet

'); - }); - - it('should escape snippet without any filter', function() { - expect(using('#escaped-html').element('div').html()). - toBe("<p style=\"color:blue\">an html\n" + - "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + - "snippet</p>"); - }); - - it('should inline raw snippet if filtered as unsafe', function() { - expect(using('#html-unsafe-filter').element("div").html()). - toBe("

an html\n" + - "click here\n" + - "snippet

"); - }); - - it('should update', function() { - input('snippet').enter('new text'); - expect(using('#html-filter').binding('snippet')).toBe('new text'); - expect(using('#escaped-html').element('div').html()).toBe("new <b>text</b>"); - expect(using('#html-unsafe-filter').binding("snippet")).toBe('new text'); - }); -
-
- */ -var $sanitize = function(html) { - var buf = []; - htmlParser(html, htmlSanitizeWriter(buf)); - return buf.join(''); -}; - - -// Regular Expressions for parsing tags and attributes -var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/, - END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/, - ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, - BEGIN_TAG_REGEXP = /^/g, - CDATA_REGEXP = //g, - URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/, - NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character) - - -// Good source of info about elements and attributes -// http://dev.w3.org/html5/spec/Overview.html#semantics -// http://simon.html5.org/html-elements - -// Safe Void Elements - HTML5 -// http://dev.w3.org/html5/spec/Overview.html#void-elements -var voidElements = makeMap("area,br,col,hr,img,wbr"); - -// Elements that you can, intentionally, leave open (and which close themselves) -// http://dev.w3.org/html5/spec/Overview.html#optional-tags -var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), - optionalEndTagInlineElements = makeMap("rp,rt"), - optionalEndTagElements = angular.extend({}, optionalEndTagInlineElements, optionalEndTagBlockElements); - -// Safe Block Elements - HTML5 -var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article,aside," + - "blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6," + - "header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")); - -// Inline Elements - HTML5 -var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b,bdi,bdo," + - "big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small," + - "span,strike,strong,sub,sup,time,tt,u,var")); - - -// Special Elements (can contain anything) -var specialElements = makeMap("script,style"); - -var validElements = angular.extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements); - -//Attributes that have href and hence need to be sanitized -var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap"); -var validAttrs = angular.extend({}, uriAttrs, makeMap( - 'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ - 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+ - 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+ - 'scope,scrolling,shape,span,start,summary,target,title,type,'+ - 'valign,value,vspace,width')); - -function makeMap(str) { - var obj = {}, items = str.split(','), i; - for (i = 0; i < items.length; i++) obj[items[i]] = true; - return obj; -} - - -/** - * @example - * htmlParser(htmlString, { - * start: function(tag, attrs, unary) {}, - * end: function(tag) {}, - * chars: function(text) {}, - * comment: function(text) {} - * }); - * - * @param {string} html string - * @param {object} handler - */ -function htmlParser( html, handler ) { - var index, chars, match, stack = [], last = html; - stack.last = function() { return stack[ stack.length - 1 ]; }; - - while ( html ) { - chars = true; - - // Make sure we're not in a script or style element - if ( !stack.last() || !specialElements[ stack.last() ] ) { - - // Comment - if ( html.indexOf(""); - - if ( index >= 0 ) { - if (handler.comment) handler.comment( html.substring( 4, index ) ); - html = html.substring( index + 3 ); - chars = false; - } - - // end tag - } else if ( BEGING_END_TAGE_REGEXP.test(html) ) { - match = html.match( END_TAG_REGEXP ); - - if ( match ) { - html = html.substring( match[0].length ); - match[0].replace( END_TAG_REGEXP, parseEndTag ); - chars = false; - } - - // start tag - } else if ( BEGIN_TAG_REGEXP.test(html) ) { - match = html.match( START_TAG_REGEXP ); - - if ( match ) { - html = html.substring( match[0].length ); - match[0].replace( START_TAG_REGEXP, parseStartTag ); - chars = false; - } - } - - if ( chars ) { - index = html.indexOf("<"); - - var text = index < 0 ? html : html.substring( 0, index ); - html = index < 0 ? "" : html.substring( index ); - - if (handler.chars) handler.chars( decodeEntities(text) ); - } - - } else { - html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), function(all, text){ - text = text. - replace(COMMENT_REGEXP, "$1"). - replace(CDATA_REGEXP, "$1"); - - if (handler.chars) handler.chars( decodeEntities(text) ); - - return ""; - }); - - parseEndTag( "", stack.last() ); - } - - if ( html == last ) { - throw "Parse Error: " + html; - } - last = html; - } - - // Clean up any remaining tags - parseEndTag(); - - function parseStartTag( tag, tagName, rest, unary ) { - tagName = angular.lowercase(tagName); - if ( blockElements[ tagName ] ) { - while ( stack.last() && inlineElements[ stack.last() ] ) { - parseEndTag( "", stack.last() ); - } - } - - if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) { - parseEndTag( "", tagName ); - } - - unary = voidElements[ tagName ] || !!unary; - - if ( !unary ) - stack.push( tagName ); - - var attrs = {}; - - rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) { - var value = doubleQuotedValue - || singleQoutedValue - || unqoutedValue - || ''; - - attrs[name] = decodeEntities(value); - }); - if (handler.start) handler.start( tagName, attrs, unary ); - } - - function parseEndTag( tag, tagName ) { - var pos = 0, i; - tagName = angular.lowercase(tagName); - if ( tagName ) - // Find the closest opened tag of the same type - for ( pos = stack.length - 1; pos >= 0; pos-- ) - if ( stack[ pos ] == tagName ) - break; - - if ( pos >= 0 ) { - // Close all the open elements, up the stack - for ( i = stack.length - 1; i >= pos; i-- ) - if (handler.end) handler.end( stack[ i ] ); - - // Remove the open elements from the stack - stack.length = pos; - } - } -} - -/** - * decodes all entities into regular string - * @param value - * @returns {string} A string with decoded entities. - */ -var hiddenPre=document.createElement("pre"); -function decodeEntities(value) { - hiddenPre.innerHTML=value.replace(//g, '>'); -} - -/** - * create an HTML/XML writer which writes to buffer - * @param {Array} buf use buf.jain('') to get out sanitized html string - * @returns {object} in the form of { - * start: function(tag, attrs, unary) {}, - * end: function(tag) {}, - * chars: function(text) {}, - * comment: function(text) {} - * } - */ -function htmlSanitizeWriter(buf){ - var ignore = false; - var out = angular.bind(buf, buf.push); - return { - start: function(tag, attrs, unary){ - tag = angular.lowercase(tag); - if (!ignore && specialElements[tag]) { - ignore = tag; - } - if (!ignore && validElements[tag] == true) { - out('<'); - out(tag); - angular.forEach(attrs, function(value, key){ - var lkey=angular.lowercase(key); - if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) { - out(' '); - out(key); - out('="'); - out(encodeEntities(value)); - out('"'); - } - }); - out(unary ? '/>' : '>'); - } - }, - end: function(tag){ - tag = angular.lowercase(tag); - if (!ignore && validElements[tag] == true) { - out(''); - } - if (tag == ignore) { - ignore = false; - } - }, - chars: function(chars){ - if (!ignore) { - out(encodeEntities(chars)); - } - } - }; -} - - -// define ngSanitize module and register $sanitize service -angular.module('ngSanitize', []).value('$sanitize', $sanitize); - -/** - * @ngdoc directive - * @name ngSanitize.directive:ngBindHtml - * - * @description - * Creates a binding that will sanitize the result of evaluating the `expression` with the - * {@link ngSanitize.$sanitize $sanitize} service and innerHTML the result into the current element. - * - * See {@link ngSanitize.$sanitize $sanitize} docs for examples. - * - * @element ANY - * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. - */ -angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) { - return function(scope, element, attr) { - element.addClass('ng-binding').data('$binding', attr.ngBindHtml); - scope.$watch(attr.ngBindHtml, function ngBindHtmlWatchAction(value) { - value = $sanitize(value); - element.html(value || ''); - }); - }; -}]); - -/** - * @ngdoc filter - * @name ngSanitize.filter:linky - * @function - * - * @description - * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and - * plain email address links. - * - * @param {string} text Input text. - * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in. - * @returns {string} Html-linkified text. - * - * @usage - - * - * @example - - - -
- Snippet: - - - - - - - - - - - - - - - - - - - - - -
FilterSourceRendered
linky filter -
<div ng-bind-html="snippet | linky">
</div>
-
-
-
linky target -
<div ng-bind-html="snippetWithTarget | linky:'_blank'">
</div>
-
-
-
no filter
<div ng-bind="snippet">
</div>
- - - it('should linkify the snippet with urls', function() { - expect(using('#linky-filter').binding('snippet | linky')). - toBe('Pretty text with some links: ' + - 'http://angularjs.org/, ' + - 'us@somewhere.org, ' + - 'another@somewhere.org, ' + - 'and one more: ftp://127.0.0.1/.'); - }); - - it ('should not linkify snippet without the linky filter', function() { - expect(using('#escaped-html').binding('snippet')). - toBe("Pretty text with some links:\n" + - "http://angularjs.org/,\n" + - "mailto:us@somewhere.org,\n" + - "another@somewhere.org,\n" + - "and one more: ftp://127.0.0.1/."); - }); - - it('should update', function() { - input('snippet').enter('new http://link.'); - expect(using('#linky-filter').binding('snippet | linky')). - toBe('new http://link.'); - expect(using('#escaped-html').binding('snippet')).toBe('new http://link.'); - }); - - it('should work with the target property', function() { - expect(using('#linky-target').binding("snippetWithTarget | linky:'_blank'")). - toBe('http://angularjs.org/'); - }); - - - */ -angular.module('ngSanitize').filter('linky', function() { - var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/, - MAILTO_REGEXP = /^mailto:/; - - return function(text, target) { - if (!text) return text; - var match; - var raw = text; - var html = []; - // TODO(vojta): use $sanitize instead - var writer = htmlSanitizeWriter(html); - var url; - var i; - var properties = {}; - if (angular.isDefined(target)) { - properties.target = target; - } - while ((match = raw.match(LINKY_URL_REGEXP))) { - // We can not end in these as they are sometimes found at the end of the sentence - url = match[0]; - // if we did not match ftp/http/mailto then assume mailto - if (match[2] == match[3]) url = 'mailto:' + url; - i = match.index; - writer.chars(raw.substr(0, i)); - properties.href = url; - writer.start('a', properties); - writer.chars(match[0].replace(MAILTO_REGEXP, '')); - writer.end('a'); - raw = raw.substring(i + match[0].length); - } - writer.chars(raw); - return html.join(''); - }; -}); - - -})(window, window.angular); diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-sanitize.min.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-sanitize.min.js deleted file mode 100644 index 593c4ef6d6..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-sanitize.min.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - AngularJS v1.1.5 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(I,h){'use strict';function i(a){var d={},a=a.split(","),c;for(c=0;c=0;c--)if(e[c]==b)break;if(c>=0){for(g=e.length-1;g>=c;g--)d.end&&d.end(e[g]);e.length= -c}}var b,f,e=[],j=a;for(e.last=function(){return e[e.length-1]};a;){f=!0;if(!e.last()||!q[e.last()]){if(a.indexOf("<\!--")===0)b=a.indexOf("--\>"),b>=0&&(d.comment&&d.comment(a.substring(4,b)),a=a.substring(b+3),f=!1);else if(B.test(a)){if(b=a.match(r))a=a.substring(b[0].length),b[0].replace(r,g),f=!1}else if(C.test(a)&&(b=a.match(s)))a=a.substring(b[0].length),b[0].replace(s,c),f=!1;f&&(b=a.indexOf("<"),f=b<0?a:a.substring(0,b),a=b<0?"":a.substring(b),d.chars&&d.chars(k(f)))}else a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+ -e.last()+"[^>]*>","i"),function(a,b){b=b.replace(D,"$1").replace(E,"$1");d.chars&&d.chars(k(b));return""}),g("",e.last());if(a==j)throw"Parse Error: "+a;j=a}g()}function k(a){l.innerHTML=a.replace(//g,">")}function u(a){var d=!1,c=h.bind(a,a.push);return{start:function(a,b,f){a=h.lowercase(a);!d&&q[a]&&(d=a);!d&&v[a]== -!0&&(c("<"),c(a),h.forEach(b,function(a,b){var d=h.lowercase(b);if(G[d]==!0&&(w[d]!==!0||a.match(H)))c(" "),c(b),c('="'),c(t(a)),c('"')}),c(f?"/>":">"))},end:function(a){a=h.lowercase(a);!d&&v[a]==!0&&(c(""));a==d&&(d=!1)},chars:function(a){d||c(t(a))}}}var s=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,r=/^<\s*\/\s*([\w:-]+)[^>]*>/,A=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,C=/^/g, -E=//g,H=/^((ftp|https?):\/\/|mailto:|tel:|#)/,F=/([^\#-~| |!])/g,p=i("area,br,col,hr,img,wbr"),x=i("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),y=i("rp,rt"),o=h.extend({},y,x),m=h.extend({},x,i("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),n=h.extend({},y,i("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")), -q=i("script,style"),v=h.extend({},p,m,n,o),w=i("background,cite,href,longdesc,src,usemap"),G=h.extend({},w,i("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,span,start,summary,target,title,type,valign,value,vspace,width")),l=document.createElement("pre");h.module("ngSanitize",[]).value("$sanitize",function(a){var d=[]; -z(a,u(d));return d.join("")});h.module("ngSanitize").directive("ngBindHtml",["$sanitize",function(a){return function(d,c,g){c.addClass("ng-binding").data("$binding",g.ngBindHtml);d.$watch(g.ngBindHtml,function(b){b=a(b);c.html(b||"")})}}]);h.module("ngSanitize").filter("linky",function(){var a=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,d=/^mailto:/;return function(c,g){if(!c)return c;var b,f=c,e=[],j=u(e),i,k,l={};if(h.isDefined(g))l.target=g;for(;b=f.match(a);)i= -b[0],b[2]==b[3]&&(i="mailto:"+i),k=b.index,j.chars(f.substr(0,k)),l.href=i,j.start("a",l),j.chars(b[0].replace(d,"")),j.end("a"),f=f.substring(k+b[0].length);j.chars(f);return e.join("")}})})(window,window.angular); diff --git a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-scenario.js b/ldapadmin/src/main/webapp/privateui/lib/angular/angular-scenario.js deleted file mode 100644 index 3ead0033d0..0000000000 --- a/ldapadmin/src/main/webapp/privateui/lib/angular/angular-scenario.js +++ /dev/null @@ -1,28463 +0,0 @@ -/*! - * jQuery JavaScript Library v1.8.2 - * http://jquery.com/ - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: Thu Sep 20 2012 21:13:05 GMT-0400 (Eastern Daylight Time) - */ -(function( window, undefined ) { -'use strict'; -var - // A central reference to the root jQuery(document) - rootjQuery, - - // The deferred used on DOM ready - readyList, - - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - location = window.location, - navigator = window.navigator, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // Save a reference to some core methods - core_push = Array.prototype.push, - core_slice = Array.prototype.slice, - core_indexOf = Array.prototype.indexOf, - core_toString = Object.prototype.toString, - core_hasOwn = Object.prototype.hasOwnProperty, - core_trim = String.prototype.trim, - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); - }, - - // Used for matching numbers - core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source, - - // Used for detecting and trimming whitespace - core_rnotwhite = /\S/, - core_rspace = /\s+/, - - // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, - - // JSON RegExp - rvalidchars = /^[\],:{}\s]*$/, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, - rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, - rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return ( letter + "" ).toUpperCase(); - }, - - // The ready event handler and self cleanup method - DOMContentLoaded = function() { - if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - } else if ( document.readyState === "complete" ) { - // we're here because readyState === "complete" in oldIE - // which is good enough for us to call the dom ready! - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - jQuery.ready(); - } - }, - - // [[Class]] -> type pairs - class2type = {}; - -jQuery.fn = jQuery.prototype = { - constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem, ret, doc; - - // Handle $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( 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; - doc = ( context && context.nodeType ? context.ownerDocument || context : document ); - - // scripts is true for back-compat - selector = jQuery.parseHTML( match[1], doc, true ); - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - this.attr.call( selector, context, true ); - } - - return jQuery.merge( this, selector ); - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", - - // The current version of jQuery being used - jquery: "1.8.2", - - // The default length of a jQuery object is 0 - length: 0, - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - toArray: function() { - return core_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 num == null ? - - // Return a 'clean' array - this.toArray() : - - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - - // 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; - - ret.context = this.context; - - if ( name === "find" ) { - ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - ready: function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; - }, - - eq: function( i ) { - i = +i; - return i === -1 ? - this.slice( i ) : - this.slice( i, i + 1 ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - slice: function() { - return this.pushStack( core_slice.apply( this, arguments ), - "slice", core_slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: core_push, - sort: [].sort, - splice: [].splice -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -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; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - 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 = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.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({ - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // 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, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // 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; - } - - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready, 1 ); - } - - // 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 ] ); - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger("ready").off("ready"); - } - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, - - isWindow: function( obj ) { - return obj != null && obj == obj.window; - }, - - isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, - - type: function( obj ) { - return obj == null ? - String( obj ) : - class2type[ core_toString.call(obj) ] || "object"; - }, - - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - try { - // Not own constructor property must be Object - if ( obj.constructor && - !core_hasOwn.call(obj, "constructor") && - !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || core_hasOwn.call( obj, key ); - }, - - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw new Error( msg ); - }, - - // data: string of html - // context (optional): If specified, the fragment will be created in this context, defaults to document - // scripts (optional): If true, will include scripts passed in the html string - parseHTML: function( data, context, scripts ) { - var parsed; - if ( !data || typeof data !== "string" ) { - return null; - } - if ( typeof context === "boolean" ) { - scripts = context; - context = 0; - } - context = context || document; - - // Single tag - if ( (parsed = rsingleTag.exec( data )) ) { - return [ context.createElement( parsed[1] ) ]; - } - - parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] ); - return jQuery.merge( [], - (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes ); - }, - - parseJSON: function( data ) { - if ( !data || typeof data !== "string") { - return null; - } - - // Make sure leading/trailing whitespace is removed (IE can't handle it) - data = jQuery.trim( data ); - - // Attempt to parse using the native JSON parser first - if ( window.JSON && window.JSON.parse ) { - return window.JSON.parse( data ); - } - - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( rvalidchars.test( data.replace( rvalidescape, "@" ) - .replace( rvalidtokens, "]" ) - .replace( rvalidbraces, "")) ) { - - return ( new Function( "return " + data ) )(); - - } - jQuery.error( "Invalid JSON: " + data ); - }, - - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - if ( !data || typeof data !== "string" ) { - return null; - } - try { - if ( window.DOMParser ) { // Standard - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } else { // IE - xml = new ActiveXObject( "Microsoft.XMLDOM" ); - xml.async = "false"; - xml.loadXML( data ); - } - } catch( e ) { - xml = undefined; - } - if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, - - noop: function() {}, - - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && core_rnotwhite.test( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - }, - - // args is for internal usage only - each: function( obj, callback, args ) { - var name, - i = 0, - length = obj.length, - isObj = length === undefined || jQuery.isFunction( obj ); - - if ( args ) { - if ( isObj ) { - for ( name in obj ) { - if ( callback.apply( obj[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( obj[ i++ ], args ) === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in obj ) { - if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) { - break; - } - } - } - } - - return obj; - }, - - // Use native String.trim function wherever possible - trim: core_trim && !core_trim.call("\uFEFF\xA0") ? - function( text ) { - return text == null ? - "" : - core_trim.call( text ); - } : - - // Otherwise use our own trimming functionality - function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var type, - ret = results || []; - - if ( arr != null ) { - // The window, strings (and functions) also have 'length' - // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 - type = jQuery.type( arr ); - - if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) { - core_push.call( ret, arr ); - } else { - jQuery.merge( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - var len; - - if ( arr ) { - if ( core_indexOf ) { - return core_indexOf.call( arr, elem, i ); - } - - len = arr.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; - - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in arr && arr[ i ] === elem ) { - return i; - } - } - } - - return -1; - }, - - merge: function( first, second ) { - var l = second.length, - i = first.length, - j = 0; - - if ( typeof l === "number" ) { - for ( ; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, inv ) { - var retVal, - ret = [], - i = 0, - length = elems.length; - inv = !!inv; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, key, - ret = [], - i = 0, - length = elems.length, - // jquery objects are treated as arrays - isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; - - // Go through the array, translating each of the items to their - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - // Go through every key on the object, - } else { - for ( key in elems ) { - value = callback( elems[ key ], key, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - } - - // Flatten any nested arrays - return ret.concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - 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 ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = core_slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context, args.concat( core_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; - }, - - // Multifunctional method to get and set values of a collection - // The value/s can optionally be executed if it's a function - access: function( elems, fn, key, value, chainable, emptyGet, pass ) { - var exec, - bulk = key == null, - i = 0, - length = elems.length; - - // Sets many values - if ( key && typeof key === "object" ) { - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); - } - chainable = 1; - - // Sets one value - } else if ( value !== undefined ) { - // Optionally, function values get executed if exec is true - exec = pass === undefined && jQuery.isFunction( value ); - - if ( bulk ) { - // Bulk operations only iterate when executing function values - if ( exec ) { - exec = fn; - fn = function( elem, key, value ) { - return exec.call( jQuery( elem ), value ); - }; - - // Otherwise they run against the entire set - } else { - fn.call( elems, value ); - fn = null; - } - } - - if ( fn ) { - for (; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); - } - } - - chainable = 1; - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; - }, - - now: function() { - return ( new Date() ).getTime(); - } -}); - -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready, 1 ); - - // Standards-based browsers support DOMContentLoaded - } else if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); - - // If IE event model is used - } else { - // Ensure firing before onload, maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var top = false; - - try { - top = window.frameElement == null && document.documentElement; - } catch(e) {} - - if ( top && top.doScroll ) { - (function doScrollCheck() { - if ( !jQuery.isReady ) { - - try { - // Use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - top.doScroll("left"); - } catch(e) { - return setTimeout( doScrollCheck, 50 ); - } - - // and execute any waiting functions - jQuery.ready(); - } - })(); - } - } - } - return readyList.promise( obj ); -}; - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache -function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.split( core_rspace ), 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" ? - ( optionsCache[ options ] || createOptions( options ) ) : - jQuery.extend( {}, options ); - - var // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], - // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; - } else { - self.disable(); - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { - jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" && ( !options.unique || !self.has( arg ) ) ) { - list.push( arg ); - } else if ( arg && arg.length && type !== "string" ) { - // Inspect recursively - add( arg ); - } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Control if a given callback is in the list - has: function( fn ) { - return jQuery.inArray( fn, list ) > -1; - }, - // Remove all callbacks from the list - empty: function() { - list = []; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( list && ( !fired || stack ) ) { - if ( firing ) { - stack.push( args ); - } else { - fire( args ); - } - } - 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; -}; -jQuery.extend({ - - Deferred: function( func ) { - var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - then: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - return jQuery.Deferred(function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - var action = tuple[ 0 ], - fn = fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ]( jQuery.isFunction( fn ) ? - function() { - var returned = fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); - } - } : - newDefer[ action ] - ); - }); - fns = null; - }).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 = {}; - - // Keep pipe for back-compat - promise.pipe = promise.then; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; - - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; - - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; - - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } - - // deferred[ resolve | reject | notify ] = list.fire - deferred[ tuple[0] ] = list.fire; - 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( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = core_slice.call( arguments ), - length = resolveValues.length, - - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; - if( values === progressValues ) { - deferred.notifyWith( contexts, values ); - } else if ( !( --remaining ) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, - - progressValues, progressContexts, resolveContexts; - - // add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); - } else { - --remaining; - } - } - } - - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } - - return deferred.promise(); - } -}); -jQuery.support = (function() { - - var support, - all, - a, - select, - opt, - input, - fragment, - eventName, - i, - isSupported, - clickFn, - div = document.createElement("div"); - - // Preliminary tests - div.setAttribute( "className", "t" ); - div.innerHTML = "
a"; - - all = div.getElementsByTagName("*"); - a = div.getElementsByTagName("a")[ 0 ]; - a.style.cssText = "top:1px;float:left;opacity:.5"; - - // Can't get basic test support - if ( !all || !all.length ) { - return {}; - } - - // First batch of supports tests - select = document.createElement("select"); - opt = select.appendChild( document.createElement("option") ); - input = div.getElementsByTagName("input")[ 0 ]; - - support = { - // IE strips leading whitespace when .innerHTML is used - leadingWhitespace: ( div.firstChild.nodeType === 3 ), - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - tbody: !div.getElementsByTagName("tbody").length, - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - htmlSerialize: !!div.getElementsByTagName("link").length, - - // Get the style information from getAttribute - // (IE uses .cssText instead) - style: /top/.test( a.getAttribute("style") ), - - // Make sure that URLs aren't manipulated - // (IE normalizes it by default) - hrefNormalized: ( a.getAttribute("href") === "/a" ), - - // Make sure that element opacity exists - // (IE uses filter instead) - // Use a regex to work around a WebKit issue. See #5145 - opacity: /^0.5/.test( a.style.opacity ), - - // Verify style float existence - // (IE uses styleFloat instead of cssFloat) - cssFloat: !!a.style.cssFloat, - - // Make sure that if no value is specified for a checkbox - // that it defaults to "on". - // (WebKit defaults to "" instead) - checkOn: ( input.value === "on" ), - - // Make sure that a selected-by-default option has a working selected property. - // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) - optSelected: opt.selected, - - // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) - getSetAttribute: div.className !== "t", - - // Tests for enctype support on a form(#6743) - enctype: !!document.createElement("form").enctype, - - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", - - // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode - boxModel: ( document.compatMode === "CSS1Compat" ), - - // Will be defined later - submitBubbles: true, - changeBubbles: true, - focusinBubbles: false, - deleteExpando: true, - noCloneEvent: true, - inlineBlockNeedsLayout: false, - shrinkWrapBlocks: false, - reliableMarginRight: true, - boxSizingReliable: true, - pixelPosition: false - }; - - // Make sure checked status is properly cloned - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; - - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; - - // Test to see if it's possible to delete an expando from an element - // Fails in Internet Explorer - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - - if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { - div.attachEvent( "onclick", clickFn = function() { - // Cloning a node shouldn't copy over any - // bound event handlers (IE does this) - support.noCloneEvent = false; - }); - div.cloneNode( true ).fireEvent("onclick"); - div.detachEvent( "onclick", clickFn ); - } - - // Check if a radio maintains its value - // after being appended to the DOM - input = document.createElement("input"); - input.value = "t"; - input.setAttribute( "type", "radio" ); - support.radioValue = input.value === "t"; - - input.setAttribute( "checked", "checked" ); - - // #11217 - WebKit loses check when the name is after the checked attribute - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - fragment = document.createDocumentFragment(); - fragment.appendChild( div.lastChild ); - - // WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - support.appendChecked = input.checked; - - fragment.removeChild( input ); - fragment.appendChild( div ); - - // Technique from Juriy Zaytsev - // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ - // We only care about the case where non-standard event systems - // are used, namely in IE. Short-circuiting here helps us to - // avoid an eval call (in setAttribute) which can cause CSP - // to go haywire. See: https://developer.mozilla.org/en/Security/CSP - if ( div.attachEvent ) { - for ( i in { - submit: true, - change: true, - focusin: true - }) { - eventName = "on" + i; - isSupported = ( eventName in div ); - if ( !isSupported ) { - div.setAttribute( eventName, "return;" ); - isSupported = ( typeof div[ eventName ] === "function" ); - } - support[ i + "Bubbles" ] = isSupported; - } - } - - // Run tests that need a body at doc ready - jQuery(function() { - var container, div, tds, marginDiv, - divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;", - body = document.getElementsByTagName("body")[0]; - - if ( !body ) { - // Return for frameset docs that don't have a body - return; - } - - container = document.createElement("div"); - container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px"; - body.insertBefore( container, body.firstChild ); - - // Construct the test element - div = document.createElement("div"); - container.appendChild( div ); - - // Check if table cells still have offsetWidth/Height when they are set - // to display:none and there are still other visible table cells in a - // table row; if so, offsetWidth/Height are not reliable for use when - // determining if an element has been hidden directly using - // display:none (it is still safe to use offsets if a parent element is - // hidden; don safety goggles and see bug #4512 for more information). - // (only IE 8 fails this test) - div.innerHTML = "
t
"; - tds = div.getElementsByTagName("td"); - tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; - isSupported = ( tds[ 0 ].offsetHeight === 0 ); - - tds[ 0 ].style.display = ""; - tds[ 1 ].style.display = "none"; - - // Check if empty table cells still have offsetWidth/Height - // (IE <= 8 fail this test) - support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); - - // Check box-sizing and margin behavior - div.innerHTML = ""; - div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; - support.boxSizing = ( div.offsetWidth === 4 ); - support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); - - // NOTE: To any future maintainer, we've window.getComputedStyle - // because jsdom on node.js will break without it. - if ( window.getComputedStyle ) { - support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; - support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; - - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. For more - // info see bug #3333 - // Fails in WebKit before Feb 2011 nightlies - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - marginDiv = document.createElement("div"); - marginDiv.style.cssText = div.style.cssText = divReset; - marginDiv.style.marginRight = marginDiv.style.width = "0"; - div.style.width = "1px"; - div.appendChild( marginDiv ); - support.reliableMarginRight = - !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); - } - - if ( typeof div.style.zoom !== "undefined" ) { - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - // (IE < 8 does this) - div.innerHTML = ""; - div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; - support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); - - // Check if elements with layout shrink-wrap their children - // (IE 6 does this) - div.style.display = "block"; - div.style.overflow = "visible"; - div.innerHTML = "
"; - div.firstChild.style.width = "5px"; - support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); - - container.style.zoom = 1; - } - - // Null elements to avoid leaks in IE - body.removeChild( container ); - container = div = tds = marginDiv = null; - }); - - // Null elements to avoid leaks in IE - fragment.removeChild( div ); - all = a = select = opt = input = fragment = div = null; - - return support; -})(); -var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, - rmultiDash = /([A-Z])/g; - -jQuery.extend({ - cache: {}, - - deletedIds: [], - - // Remove at next major release (1.9/2.0) - uuid: 0, - - // Unique for each copy of jQuery on the page - // Non-digits removed to match rinlinejQuery - expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), - - // The following elements throw uncatchable exceptions if you - // attempt to add expando properties to them. - noData: { - "embed": true, - // Ban all objects except for Flash (which handle expandos) - "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", - "applet": true - }, - - hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); - }, - - data: function( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, ret, - internalKey = jQuery.expando, - getByName = typeof name === "string", - - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, - - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, - - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; - - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) { - return; - } - - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++; - } else { - id = internalKey; - } - } - - if ( !cache[ id ] ) { - cache[ id ] = {}; - - // Avoids exposing jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - if ( !isNode ) { - cache[ id ].toJSON = jQuery.noop; - } - } - - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = jQuery.extend( cache[ id ].data, name ); - } - } - - thisCache = cache[ id ]; - - // jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } - - thisCache = thisCache.data; - } - - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } - - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( getByName ) { - - // First Try to find as-is property data - ret = thisCache[ name ]; - - // Test for null|undefined property data - if ( ret == null ) { - - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } - - return ret; - }, - - removeData: function( elem, name, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, i, l, - - isNode = elem.nodeType, - - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - id = isNode ? elem[ jQuery.expando ] : jQuery.expando; - - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } - - if ( name ) { - - thisCache = pvt ? cache[ id ] : cache[ id ].data; - - if ( thisCache ) { - - // Support array or space separated string names for data keys - if ( !jQuery.isArray( name ) ) { - - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { - - // split the camel cased version by spaces unless a key with the spaces exists - name = jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split(" "); - } - } - } - - for ( i = 0, l = name.length; i < l; i++ ) { - delete thisCache[ name[i] ]; - } - - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { - return; - } - } - } - - // See jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; - - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject( cache[ id ] ) ) { - return; - } - } - - // Destroy the cache - if ( isNode ) { - jQuery.cleanData( [ elem ], true ); - - // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) - } else if ( jQuery.support.deleteExpando || cache != cache.window ) { - delete cache[ id ]; - - // When all else fails, null - } else { - cache[ id ] = null; - } - }, - - // For internal use only. - _data: function( elem, name, data ) { - return jQuery.data( elem, name, data, true ); - }, - - // A method for determining if a DOM node can handle the data expando - acceptData: function( elem ) { - var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; - - // nodes accept data unless otherwise specified; rejection can be conditional - return !noData || noData !== true && elem.getAttribute("classid") === noData; - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var parts, part, attr, name, l, - elem = this[0], - i = 0, - data = null; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = jQuery.data( elem ); - - if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { - attr = elem.attributes; - for ( l = attr.length; i < l; i++ ) { - name = attr[i].name; - - if ( !name.indexOf( "data-" ) ) { - name = jQuery.camelCase( name.substring(5) ); - - dataAttr( elem, name, data[ name ] ); - } - } - jQuery._data( elem, "parsedAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } - - parts = key.split( ".", 2 ); - parts[1] = parts[1] ? "." + parts[1] : ""; - part = parts[1] + "!"; - - return jQuery.access( this, function( value ) { - - if ( value === undefined ) { - data = this.triggerHandler( "getData" + part, [ parts[0] ] ); - - // Try to fetch any internally stored data first - if ( data === undefined && elem ) { - data = jQuery.data( elem, key ); - data = dataAttr( elem, key, data ); - } - - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - } - - parts[1] = value; - this.each(function() { - var self = jQuery( this ); - - self.triggerHandler( "setData" + part, parts ); - jQuery.data( this, key, value ); - self.triggerHandler( "changeData" + part, parts ); - }); - }, null, value, arguments.length > 1, null, false ); - }, - - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); - -function dataAttr( elem, key, data ) { - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - jQuery.data( elem, key, data ); - - } else { - data = undefined; - } - } - - return data; -} - -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { - var name; - for ( name in obj ) { - - // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } - - return true; -} -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = jQuery._data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray(data) ) { - queue = jQuery._data( 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 intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return jQuery._data( elem, key ) || jQuery._data( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - jQuery.removeData( elem, type + "queue", true ); - jQuery.removeData( elem, key, true ); - }) - }); - } -}); - -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 ); - }); - }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - 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 = setTimeout( next, time ); - hooks.stop = function() { - clearTimeout( timeout ); - }; - }); - }, - 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 = jQuery._data( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -}); -var nodeHook, boolHook, fixSpecified, - rclass = /[\t\r\n]/g, - rreturn = /\r/g, - rtype = /^(?:button|input)$/i, - rfocusable = /^(?:button|input|object|select|textarea)$/i, - rclickable = /^a(?:rea|)$/i, - rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, - getSetAttribute = jQuery.support.getSetAttribute; - -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each(function() { - jQuery.removeAttr( this, name ); - }); - }, - - prop: function( name, value ) { - return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - name = jQuery.propFix[ name ] || name; - return this.each(function() { - // try/catch handles cases where IE balks (such as removing a property on window) - try { - this[ name ] = undefined; - delete this[ name ]; - } catch( e ) {} - }); - }, - - addClass: function( value ) { - var classNames, i, l, elem, - setClass, c, cl; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).addClass( value.call(this, j, this.className) ); - }); - } - - if ( value && typeof value === "string" ) { - classNames = value.split( core_rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 ) { - if ( !elem.className && classNames.length === 1 ) { - elem.className = value; - - } else { - setClass = " " + elem.className + " "; - - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) { - setClass += classNames[ c ] + " "; - } - } - elem.className = jQuery.trim( setClass ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - var removes, className, elem, c, cl, i, l; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).removeClass( value.call(this, j, this.className) ); - }); - } - if ( (value && typeof value === "string") || value === undefined ) { - removes = ( value || "" ).split( core_rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - if ( elem.nodeType === 1 && elem.className ) { - - className = (" " + elem.className + " ").replace( rclass, " " ); - - // loop over each item in the removal list - for ( c = 0, cl = removes.length; c < cl; c++ ) { - // Remove until there is nothing to remove, - while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) { - className = className.replace( " " + removes[ c ] + " " , " " ); - } - } - elem.className = value ? jQuery.trim( className ) : ""; - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isBool = typeof stateVal === "boolean"; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( i ) { - jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = jQuery( this ), - state = stateVal, - classNames = value.split( core_rspace ); - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space separated list - state = isBool ? state : !self.hasClass( className ); - self[ state ? "addClass" : "removeClass" ]( className ); - } - - } else if ( type === "undefined" || type === "boolean" ) { - if ( this.className ) { - // store className if set - jQuery._data( this, "__className__", this.className ); - } - - // toggle whole className - this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " ", - i = 0, - l = this.length; - for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - var hooks, ret, isFunction, - 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; - - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; - } - - return; - } - - isFunction = jQuery.isFunction( value ); - - return this.each(function( i ) { - var val, - self = jQuery(this); - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, self.val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( jQuery.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 ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, i, max, option, - index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } - - // Loop through all the selected options - i = one ? index : 0; - max = one ? index + 1 : options.length; - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && - (!option.parentNode.disabled || !jQuery.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 ); - } - } - - // Fixes Bug #2551 -- select.val() broken in IE after form.reset() - if ( one && !values.length && options.length ) { - return jQuery( options[ index ] ).val(); - } - - return values; - }, - - set: function( elem, value ) { - var values = jQuery.makeArray( value ); - - jQuery(elem).find("option").each(function() { - this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - elem.selectedIndex = -1; - } - return values; - } - } - }, - - // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9 - attrFn: {}, - - attr: function( elem, name, value, pass ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) { - return jQuery( elem )[ name ]( value ); - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - // All attributes are lowercase - // Grab necessary hook if one is defined - if ( notxml ) { - name = name.toLowerCase(); - hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); - } - - if ( value !== undefined ) { - - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - - } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - elem.setAttribute( name, value + "" ); - return value; - } - - } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - - ret = elem.getAttribute( name ); - - // Non-existent attributes return null, we normalize to undefined - return ret === null ? - undefined : - ret; - } - }, - - removeAttr: function( elem, value ) { - var propName, attrNames, name, isBool, - i = 0; - - if ( value && elem.nodeType === 1 ) { - - attrNames = value.split( core_rspace ); - - for ( ; i < attrNames.length; i++ ) { - name = attrNames[ i ]; - - if ( name ) { - propName = jQuery.propFix[ name ] || name; - isBool = rboolean.test( name ); - - // See #9699 for explanation of this approach (setting first, then removal) - // Do not do this for boolean attributes (see #10870) - if ( !isBool ) { - jQuery.attr( elem, name, "" ); - } - elem.removeAttribute( getSetAttribute ? name : propName ); - - // Set corresponding property to false for boolean attributes - if ( isBool && propName in elem ) { - elem[ propName ] = false; - } - } - } - } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - // We can't allow the type property to be changed (since it causes problems in IE) - if ( rtype.test( elem.nodeName ) && elem.parentNode ) { - jQuery.error( "type property can't be changed" ); - } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to it's default in case type is set after value - // This is for element creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - }, - // Use the value property for back compat - // Use the nodeHook for button elements in IE6/7 (#1954) - value: { - get: function( elem, name ) { - if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { - return nodeHook.get( elem, name ); - } - return name in elem ? - elem.value : - null; - }, - set: function( elem, value, name ) { - if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { - return nodeHook.set( elem, value, name ); - } - // Does not return so that setAttribute is also used - elem.value = value; - } - } - }, - - propFix: { - tabindex: "tabIndex", - readonly: "readOnly", - "for": "htmlFor", - "class": "className", - maxlength: "maxLength", - cellspacing: "cellSpacing", - cellpadding: "cellPadding", - rowspan: "rowSpan", - colspan: "colSpan", - usemap: "useMap", - frameborder: "frameBorder", - contenteditable: "contentEditable" - }, - - prop: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // 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; - - } else { - return ( elem[ name ] = value ); - } - - } else { - if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - return elem[ name ]; - } - } - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set - // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - var attributeNode = elem.getAttributeNode("tabindex"); - - return attributeNode && attributeNode.specified ? - parseInt( attributeNode.value, 10 ) : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } - } - } -}); - -// Hook for boolean attributes -boolHook = { - get: function( elem, name ) { - // Align boolean attributes with corresponding properties - // Fall back to attribute presence where some booleans are not supported - var attrNode, - property = jQuery.prop( elem, name ); - return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? - name.toLowerCase() : - undefined; - }, - set: function( elem, value, name ) { - var propName; - if ( value === false ) { - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - // value is true since we know at this point it's type boolean and not false - // Set boolean attributes to the same name and set the DOM property - propName = jQuery.propFix[ name ] || name; - if ( propName in elem ) { - // Only set the IDL specifically if it already exists on the element - elem[ propName ] = true; - } - - elem.setAttribute( name, name.toLowerCase() ); - } - return name; - } -}; - -// IE6/7 do not support getting/setting some attributes with get/setAttribute -if ( !getSetAttribute ) { - - fixSpecified = { - name: true, - id: true, - coords: true - }; - - // Use this for any attribute in IE6/7 - // This fixes almost every IE6/7 issue - nodeHook = jQuery.valHooks.button = { - get: function( elem, name ) { - var ret; - ret = elem.getAttributeNode( name ); - return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ? - ret.value : - undefined; - }, - set: function( elem, value, name ) { - // Set the existing or create a new attribute node - var ret = elem.getAttributeNode( name ); - if ( !ret ) { - ret = document.createAttribute( name ); - elem.setAttributeNode( ret ); - } - return ( ret.value = value + "" ); - } - }; - - // Set width and height to auto instead of 0 on empty string( Bug #8150 ) - // This is for removals - jQuery.each([ "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - set: function( elem, value ) { - if ( value === "" ) { - elem.setAttribute( name, "auto" ); - return value; - } - } - }); - }); - - // Set contenteditable to false on removals(#10429) - // Setting to empty string throws an error as an invalid value - jQuery.attrHooks.contenteditable = { - get: nodeHook.get, - set: function( elem, value, name ) { - if ( value === "" ) { - value = "false"; - } - nodeHook.set( elem, value, name ); - } - }; -} - - -// Some attributes require a special call on IE -if ( !jQuery.support.hrefNormalized ) { - jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - get: function( elem ) { - var ret = elem.getAttribute( name, 2 ); - return ret === null ? undefined : ret; - } - }); - }); -} - -if ( !jQuery.support.style ) { - jQuery.attrHooks.style = { - get: function( elem ) { - // Return undefined in the case of empty string - // Normalize to lowercase since IE uppercases css property names - return elem.style.cssText.toLowerCase() || undefined; - }, - set: function( elem, value ) { - return ( elem.style.cssText = value + "" ); - } - }; -} - -// Safari mis-reports the default selected property of an option -// Accessing the parent's selectedIndex property fixes it -if ( !jQuery.support.optSelected ) { - jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { - get: function( elem ) { - var parent = elem.parentNode; - - if ( parent ) { - parent.selectedIndex; - - // Make sure that it also works with optgroups, see #5701 - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - return null; - } - }); -} - -// IE6/7 call enctype encoding -if ( !jQuery.support.enctype ) { - jQuery.propFix.enctype = "encoding"; -} - -// Radios and checkboxes getter/setter -if ( !jQuery.support.checkOn ) { - jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - get: function( elem ) { - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - } - }; - }); -} -jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { - set: function( elem, value ) { - if ( jQuery.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); - } - } - }); -}); -var rformElems = /^(?:textarea|input|select)$/i, - rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/, - rhoverHack = /(?:^|\s)hover(\.\S+|)\b/, - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - hoverHack = function( events ) { - return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); - }; - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - add: function( elem, types, handler, data, selector ) { - - var elemData, eventHandle, events, - t, tns, type, namespaces, handleObj, - handleObjIn, handlers, special; - - // Don't attach events to noData or text/comment nodes (allow plain objects tho) - if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { - 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; - } - - // 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 - events = elemData.events; - if ( !events ) { - elemData.events = events = {}; - } - eventHandle = elemData.handle; - if ( !eventHandle ) { - elemData.handle = eventHandle = 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" && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; - }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; - } - - // Handle multiple events separated by a space - // jQuery(...).bind("mouseover mouseout", fn); - types = jQuery.trim( hoverHack(types) ).split( " " ); - for ( t = 0; t < types.length; t++ ) { - - tns = rtypenamespace.exec( types[t] ) || []; - type = tns[1]; - namespaces = ( tns[2] || "" ).split( "." ).sort(); - - // 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: tns[1], - 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 - handlers = events[ type ]; - if ( !handlers ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener/attachEvent if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + 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; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - global: {}, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var t, tns, type, origType, namespaces, origCount, - j, events, special, eventType, handleObj, - elemData = jQuery.hasData( elem ) && jQuery._data( elem ); - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = jQuery.trim( hoverHack( types || "" ) ).split(" "); - for ( t = 0; t < types.length; t++ ) { - tns = rtypenamespace.exec( types[t] ) || []; - type = origType = tns[1]; - namespaces = tns[2]; - - // 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; - eventType = events[ type ] || []; - origCount = eventType.length; - namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null; - - // Remove matching events - for ( j = 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !namespaces || namespaces.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - eventType.splice( j--, 1 ); - - if ( handleObj.selector ) { - eventType.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 ( eventType.length === 0 && origCount !== eventType.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - jQuery.removeData( elem, "events", true ); - } - }, - - // Events that are safe to short-circuit if no handlers are attached. - // Native DOM events should not be added, they may have inline handlers. - customEvent: { - "getData": true, - "setData": true, - "changeData": true - }, - - trigger: function( event, data, elem, onlyHandlers ) { - // Don't do events on text and comment nodes - if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { - return; - } - - // Event object or event type - var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType, - type = event.type || event, - namespaces = []; - - // 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( "!" ) >= 0 ) { - // Exclusive events trigger only for the exact event (no namespaces) - type = type.slice(0, -1); - exclusive = true; - } - - if ( type.indexOf( "." ) >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - - if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { - // No jQuery handlers for this event type, and it can't have inline handlers - return; - } - - // Caller can pass in an Event, Object, or just an event type string - event = typeof event === "object" ? - // jQuery.Event object - event[ jQuery.expando ] ? event : - // Object literal - new jQuery.Event( type, event ) : - // Just the event type (string) - new jQuery.Event( type ); - - event.type = type; - event.isTrigger = true; - event.exclusive = exclusive; - event.namespace = namespaces.join( "." ); - event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null; - ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; - - // Handle a global trigger - if ( !elem ) { - - // TODO: Stop taunting the data cache; remove global events and always attach to document - cache = jQuery.cache; - for ( i in cache ) { - if ( cache[ i ].events && cache[ i ].events[ type ] ) { - jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); - } - } - return; - } - - // 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 ? jQuery.makeArray( data ) : []; - data.unshift( event ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( 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) - eventPath = [[ elem, special.bindType || type ]]; - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; - for ( old = elem; cur; cur = cur.parentNode ) { - eventPath.push([ cur, bubbleType ]); - old = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( old === (elem.ownerDocument || document) ) { - eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); - } - } - - // Fire handlers on the event path - for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { - - cur = eventPath[i][0]; - event.type = eventPath[i][1]; - - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - // Note that this is a bare JS function and not a jQuery handler - handle = ontype && cur[ ontype ]; - if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === 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( elem.ownerDocument, data ) === false) && - !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction() check here because IE6/7 fails that test. - // Don't do default actions on window, that's where global variables be (#6170) - // IE<9 dies on focus/blur to hidden element (#1486) - if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - old = elem[ ontype ]; - - if ( old ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( old ) { - elem[ ontype ] = old; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event || window.event ); - - var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related, - handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), - delegateCount = handlers.delegateCount, - args = core_slice.call( arguments ), - run_all = !event.exclusive && !event.namespace, - special = jQuery.event.special[ event.type ] || {}, - handlerQueue = []; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - 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 that should run if there are delegated events - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && !(event.button && event.type === "click") ) { - - for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { - - // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.disabled !== true || event.type !== "click" ) { - selMatch = {}; - matches = []; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - sel = handleObj.selector; - - if ( selMatch[ sel ] === undefined ) { - selMatch[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) >= 0 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( selMatch[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, matches: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if ( handlers.length > delegateCount ) { - handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); - } - - // Run delegates first; they may want to stop propagation beneath us - for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { - matched = handlerQueue[ i ]; - event.currentTarget = matched.elem; - - for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { - handleObj = matched.matches[ j ]; - - // Triggered event must either 1) be non-exclusive and have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { - - event.data = handleObj.data; - event.handleObj = handleObj; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - event.result = ret; - if ( 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; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** - props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var eventDoc, doc, body, - button = original.button, - fromElement = original.fromElement; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, - originalEvent = event, - fixHook = jQuery.event.fixHooks[ event.type ] || {}, - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = jQuery.Event( originalEvent ); - - for ( i = copy.length; i; ) { - prop = copy[ --i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) - if ( !event.target ) { - event.target = originalEvent.srcElement || document; - } - - // Target should not be a text node (#504, Safari) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8) - event.metaKey = !!event.metaKey; - - return fixHook.filter? fixHook.filter( event, originalEvent ) : event; - }, - - special: { - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - - focus: { - delegateType: "focusin" - }, - blur: { - delegateType: "focusout" - }, - - beforeunload: { - setup: function( data, namespaces, eventHandle ) { - // We only want to do this special case on windows - if ( jQuery.isWindow( this ) ) { - this.onbeforeunload = eventHandle; - } - }, - - teardown: function( namespaces, eventHandle ) { - if ( this.onbeforeunload === eventHandle ) { - this.onbeforeunload = null; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -// Some plugins are using, but it's undocumented/deprecated and will be removed. -// The 1.7 special event interface should provide all the hooks needed now. -jQuery.event.handle = jQuery.event.dispatch; - -jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } - } : - function( elem, type, handle ) { - var name = "on" + type; - - if ( elem.detachEvent ) { - - // #8545, #7054, preventing memory leaks for custom events in IE6-8 – - // detachEvent needed property on element, by name of that event, to properly expose it to GC - if ( typeof elem[ name ] === "undefined" ) { - elem[ name ] = null; - } - - elem.detachEvent( name, 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.returnValue === false || - src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; - - // 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 || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -function returnFalse() { - return false; -} -function returnTrue() { - return true; -} - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - preventDefault: function() { - this.isDefaultPrevented = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - - // if preventDefault exists run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); - - // otherwise set the returnValue property of the original event to false (IE) - } else { - e.returnValue = false; - } - }, - stopPropagation: function() { - this.isPropagationStopped = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - // if stopPropagation exists run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); - } - // otherwise set the cancelBubble property of the original event to true (IE) - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj, - selector = handleObj.selector; - - // For mousenter/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; - } - }; -}); - -// IE submit delegation -if ( !jQuery.support.submitBubbles ) { - - jQuery.event.special.submit = { - setup: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Lazy-add a submit handler when a descendant form may potentially be submitted - jQuery.event.add( this, "click._submit keypress._submit", function( e ) { - // Node name check avoids a VML-related crash in IE (#9807) - var elem = e.target, - form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !jQuery._data( form, "_submit_attached" ) ) { - jQuery.event.add( form, "submit._submit", function( event ) { - event._submit_bubble = true; - }); - jQuery._data( form, "_submit_attached", true ); - } - }); - // return undefined since we don't need an event listener - }, - - postDispatch: function( event ) { - // If form was submitted by the user, bubble the event up the tree - if ( event._submit_bubble ) { - delete event._submit_bubble; - if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event, true ); - } - } - }, - - teardown: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Remove delegated handlers; cleanData eventually reaps submit handlers attached above - jQuery.event.remove( this, "._submit" ); - } - }; -} - -// IE change delegation and checkbox/radio fix -if ( !jQuery.support.changeBubbles ) { - - jQuery.event.special.change = { - - setup: function() { - - if ( rformElems.test( this.nodeName ) ) { - // IE doesn't fire change on a check/radio until blur; trigger it on click - // after a propertychange. Eat the blur-change in special.change.handle. - // This still fires onchange a second time for check/radio after blur. - if ( this.type === "checkbox" || this.type === "radio" ) { - jQuery.event.add( this, "propertychange._change", function( event ) { - if ( event.originalEvent.propertyName === "checked" ) { - this._just_changed = true; - } - }); - jQuery.event.add( this, "click._change", function( event ) { - if ( this._just_changed && !event.isTrigger ) { - this._just_changed = false; - } - // Allow triggered, simulated change events (#11500) - jQuery.event.simulate( "change", this, event, true ); - }); - } - return false; - } - // Delegated event; lazy-add a change handler on descendant inputs - jQuery.event.add( this, "beforeactivate._change", function( e ) { - var elem = e.target; - - if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) { - jQuery.event.add( elem, "change._change", function( event ) { - if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - jQuery.event.simulate( "change", this.parentNode, event, true ); - } - }); - jQuery._data( elem, "_change_attached", true ); - } - }); - }, - - handle: function( event ) { - var elem = event.target; - - // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { - return event.handleObj.handler.apply( this, arguments ); - } - }, - - teardown: function() { - jQuery.event.remove( this, "._change" ); - - return !rformElems.test( this.nodeName ); - } - }; -} - -// Create "bubbling" focus and blur events -if ( !jQuery.support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler while someone wants focusin/focusout - var attaches = 0, - handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - if ( attaches++ === 0 ) { - document.addEventListener( orig, handler, true ); - } - }, - teardown: function() { - if ( --attaches === 0 ) { - document.removeEventListener( orig, handler, true ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { // && selector != null - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - 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 this; - } - - 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 this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on( 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 ); - }); - }, - - bind: function( types, data, fn ) { - return this.on( types, null, data, fn ); - }, - unbind: function( types, fn ) { - return this.off( types, null, fn ); - }, - - live: function( types, data, fn ) { - jQuery( this.context ).on( types, this.selector, data, fn ); - return this; - }, - die: function( types, fn ) { - jQuery( this.context ).off( types, this.selector || "**", fn ); - return this; - }, - - 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 ); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - if ( this[0] ) { - return jQuery.event.trigger( type, data, this[0], true ); - } - }, - - toggle: function( fn ) { - // Save reference to arguments for access in closure - var args = arguments, - guid = fn.guid || jQuery.guid++, - i = 0, - toggler = function( event ) { - // Figure out which function to execute - var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; - jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); - - // Make sure that clicks stop - event.preventDefault(); - - // and execute the function - return args[ lastToggle ].apply( this, arguments ) || false; - }; - - // link all the functions, so any of them can unbind this click handler - toggler.guid = guid; - while ( i < args.length ) { - args[ i++ ].guid = guid; - } - - return this.click( toggler ); - }, - - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -}); - -jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( data, fn ) { - if ( fn == null ) { - fn = data; - data = null; - } - - return arguments.length > 0 ? - this.on( name, null, data, fn ) : - this.trigger( name ); - }; - - if ( rkeyEvent.test( name ) ) { - jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; - } - - if ( rmouseEvent.test( name ) ) { - jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; - } -}); -/*! - * Sizzle CSS Selector Engine - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license - * http://sizzlejs.com/ - */ -(function( window, undefined ) { - -var cachedruns, - assertGetIdNotName, - Expr, - getText, - isXML, - contains, - compile, - sortOrder, - hasDuplicate, - outermostContext, - - baseHasDuplicate = true, - strundefined = "undefined", - - expando = ( "sizcache" + Math.random() ).replace( ".", "" ), - - Token = String, - document = window.document, - docElem = document.documentElement, - dirruns = 0, - done = 0, - pop = [].pop, - push = [].push, - slice = [].slice, - // Use a stripped-down indexOf if a native one is unavailable - indexOf = [].indexOf || function( elem ) { - var i = 0, - len = this.length; - for ( ; i < len; i++ ) { - if ( this[i] === elem ) { - return i; - } - } - return -1; - }, - - // Augment a function for special use by Sizzle - markFunction = function( fn, value ) { - fn[ expando ] = value == null || value; - return fn; - }, - - createCache = function() { - var cache = {}, - keys = []; - - return markFunction(function( key, value ) { - // Only keep the most recent entries - if ( keys.push( key ) > Expr.cacheLength ) { - delete cache[ keys.shift() ]; - } - - return (cache[ key ] = value); - }, cache ); - }, - - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - - // Regex - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors - operators = "([*^$|!~]?=)", - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + - "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", - - // Prefer arguments not in parens/brackets, - // then attribute selectors and non-pseudos (denoted by :), - // then anything else - // These preferences are here to reduce the number of selectors - // needing tokenize in the PSEUDO preFilter - pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", - - // For matchExpr.POS and matchExpr.needsContext - pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + - "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), - rpseudo = new RegExp( pseudos ), - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, - - rnot = /^:not/, - rsibling = /[\x20\t\r\n\f]*[+~]/, - rendsWithNot = /:not\($/, - - rheader = /h\d/i, - rinputs = /input|select|textarea|button/i, - - rbackslash = /\\(?!\\)/g, - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "POS": new RegExp( pos, "i" ), - "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - // For use in libraries implementing .is() - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) - }, - - // Support - - // Used for testing something on an element - assert = function( fn ) { - var div = document.createElement("div"); - - try { - return fn( div ); - } catch (e) { - return false; - } finally { - // release memory in IE - div = null; - } - }, - - // Check if getElementsByTagName("*") returns only elements - assertTagNameNoComments = assert(function( div ) { - div.appendChild( document.createComment("") ); - return !div.getElementsByTagName("*").length; - }), - - // Check if getAttribute returns normalized href attributes - assertHrefNotNormalized = assert(function( div ) { - div.innerHTML = ""; - return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && - div.firstChild.getAttribute("href") === "#"; - }), - - // Check if attributes should be retrieved by attribute nodes - assertAttributes = assert(function( div ) { - div.innerHTML = ""; - var type = typeof div.lastChild.getAttribute("multiple"); - // IE8 returns a string for some attributes even when not present - return type !== "boolean" && type !== "string"; - }), - - // Check if getElementsByClassName can be trusted - assertUsableClassName = assert(function( div ) { - // Opera can't find a second classname (in 9.6) - div.innerHTML = ""; - if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { - return false; - } - - // Safari 3.2 caches class attributes and doesn't catch changes - div.lastChild.className = "e"; - return div.getElementsByClassName("e").length === 2; - }), - - // Check if getElementById returns elements by name - // Check if getElementsByName privileges form controls or returns elements by ID - assertUsableName = assert(function( div ) { - // Inject content - div.id = expando + 0; - div.innerHTML = "
"; - docElem.insertBefore( div, docElem.firstChild ); - - // Test - var pass = document.getElementsByName && - // buggy browsers will return fewer than the correct 2 - document.getElementsByName( expando ).length === 2 + - // buggy browsers will return more than the correct 0 - document.getElementsByName( expando + 0 ).length; - assertGetIdNotName = !document.getElementById( expando ); - - // Cleanup - docElem.removeChild( div ); - - return pass; - }); - -// If slice is not available, provide a backup -try { - slice.call( docElem.childNodes, 0 )[0].nodeType; -} catch ( e ) { - slice = function( i ) { - var elem, - results = []; - for ( ; (elem = this[i]); i++ ) { - results.push( elem ); - } - return results; - }; -} - -function Sizzle( selector, context, results, seed ) { - results = results || []; - context = context || document; - var match, elem, xml, m, - nodeType = context.nodeType; - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - if ( nodeType !== 1 && nodeType !== 9 ) { - return []; - } - - xml = isXML( context ); - - if ( !xml && !seed ) { - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { - push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); - return results; - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed, xml ); -} - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - return Sizzle( expr, null, null, [ elem ] ).length > 0; -}; - -// Returns a function to use in pseudos for input types -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 -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 positionals -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]); - } - } - }); - }); -} - -/** - * 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 ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (see #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 - } else { - - // If no nodeType, this is expected to be an array - for ( ; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } - return ret; -}; - -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; -}; - -// Element contains another -contains = Sizzle.contains = 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) ); - } : - docElem.compareDocumentPosition ? - function( a, b ) { - return b && !!( a.compareDocumentPosition( b ) & 16 ); - } : - function( a, b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - return false; - }; - -Sizzle.attr = function( elem, name ) { - var val, - xml = isXML( elem ); - - if ( !xml ) { - name = name.toLowerCase(); - } - if ( (val = Expr.attrHandle[ name ]) ) { - return val( elem ); - } - if ( xml || assertAttributes ) { - return elem.getAttribute( name ); - } - val = elem.getAttributeNode( name ); - return val ? - typeof elem[ name ] === "boolean" ? - elem[ name ] ? name : null : - val.specified ? val.value : null : - null; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - // IE6/7 return a modified href - attrHandle: assertHrefNotNormalized ? - {} : - { - "href": function( elem ) { - return elem.getAttribute( "href", 2 ); - }, - "type": function( elem ) { - return elem.getAttribute("type"); - } - }, - - find: { - "ID": assertGetIdNotName ? - function( id, context, xml ) { - if ( typeof context.getElementById !== strundefined && !xml ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - } : - function( id, context, xml ) { - if ( typeof context.getElementById !== strundefined && !xml ) { - var m = context.getElementById( id ); - - return m ? - m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? - [m] : - undefined : - []; - } - }, - - "TAG": assertTagNameNoComments ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - var elem, - tmp = [], - i = 0; - - for ( ; (elem = results[i]); i++ ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }, - - "NAME": assertUsableName && function( tag, context ) { - if ( typeof context.getElementsByName !== strundefined ) { - return context.getElementsByName( name ); - } - }, - - "CLASS": assertUsableClassName && function( className, context, xml ) { - if ( typeof context.getElementsByClassName !== strundefined && !xml ) { - return context.getElementsByClassName( className ); - } - } - }, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( rbackslash, "" ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 3 xn-component of xn+y argument ([+-]?\d*n|) - 4 sign of xn-component - 5 x of xn-component - 6 sign of y-component - 7 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1] === "nth" ) { - // nth-child requires argument - if ( !match[2] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); - match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); - - // other types prohibit arguments - } else if ( match[2] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var unquoted, excess; - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - if ( match[3] ) { - match[2] = match[3]; - } else if ( (unquoted = match[4]) ) { - // Only check arguments that contain a pseudo - if ( 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 - unquoted = unquoted.slice( 0, excess ); - match[0] = match[0].slice( 0, excess ); - } - match[2] = unquoted; - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - "ID": assertGetIdNotName ? - function( id ) { - id = id.replace( rbackslash, "" ); - return function( elem ) { - return elem.getAttribute("id") === id; - }; - } : - function( id ) { - id = id.replace( rbackslash, "" ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === id; - }; - }, - - "TAG": function( nodeName ) { - if ( nodeName === "*" ) { - return function() { return true; }; - } - nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); - - return function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ expando ][ className ]; - if ( !pattern ) { - pattern = classCache( className, new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)") ); - } - return function( elem ) { - return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); - }; - }, - - "ATTR": function( name, operator, check ) { - return function( elem, context ) { - 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.substr( result.length - check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, argument, first, last ) { - - if ( type === "nth" ) { - return function( elem ) { - var node, diff, - parent = elem.parentNode; - - if ( first === 1 && last === 0 ) { - return true; - } - - if ( parent ) { - diff = 0; - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - diff++; - if ( elem === node ) { - break; - } - } - } - } - - // Incorporate the offset (or cast to NaN), then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - }; - } - - return function( elem ) { - var node = elem; - - switch ( type ) { - case "only": - case "first": - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - if ( type === "first" ) { - return true; - } - - node = elem; - - /* falls through */ - case "last": - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - return true; - } - }; - }, - - "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.call( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - 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 ); - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === 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; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), - // not comment, processing instructions, or others - // Thanks to Diego Perini for the nodeName shortcut - // Greater than "@" means alpha characters (specifically not starting with "#" or "?") - var nodeType; - elem = elem.firstChild; - while ( elem ) { - if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { - return false; - } - elem = elem.nextSibling; - } - return true; - }, - - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "text": function( elem ) { - var type, attr; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && - (type = elem.type) === "text" && - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); - }, - - // Input types - "radio": createInputPseudo("radio"), - "checkbox": createInputPseudo("checkbox"), - "file": createInputPseudo("file"), - "password": createInputPseudo("password"), - "image": createInputPseudo("image"), - - "submit": createButtonPseudo("submit"), - "reset": createButtonPseudo("reset"), - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "focus": function( elem ) { - var doc = elem.ownerDocument; - return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href); - }, - - "active": function( elem ) { - return elem === elem.ownerDocument.activeElement; - }, - - // Positional types - "first": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = 0; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = 1; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -function siblingCheck( a, b, ret ) { - if ( a === b ) { - return ret; - } - - var cur = a.nextSibling; - - while ( cur ) { - if ( cur === b ) { - return -1; - } - - cur = cur.nextSibling; - } - - return 1; -} - -sortOrder = docElem.compareDocumentPosition ? - function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? - a.compareDocumentPosition : - a.compareDocumentPosition(b) & 4 - ) ? -1 : 1; - } : - function( a, b ) { - // The nodes are identical, we can exit early - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Fallback to using sourceIndex (in IE) if it's available on both nodes - } else if ( a.sourceIndex && b.sourceIndex ) { - return a.sourceIndex - b.sourceIndex; - } - - var al, bl, - ap = [], - bp = [], - aup = a.parentNode, - bup = b.parentNode, - cur = aup; - - // If the nodes are siblings (or identical) we can do a quick check - if ( aup === bup ) { - return siblingCheck( a, b ); - - // If no parents were found then the nodes are disconnected - } else if ( !aup ) { - return -1; - - } else if ( !bup ) { - return 1; - } - - // Otherwise they're somewhere else in the tree so we need - // to build up a full list of the parentNodes for comparison - while ( cur ) { - ap.unshift( cur ); - cur = cur.parentNode; - } - - cur = bup; - - while ( cur ) { - bp.unshift( cur ); - cur = cur.parentNode; - } - - al = ap.length; - bl = bp.length; - - // Start walking down the tree looking for a discrepancy - for ( var i = 0; i < al && i < bl; i++ ) { - if ( ap[i] !== bp[i] ) { - return siblingCheck( ap[i], bp[i] ); - } - } - - // We ended someplace up the tree so do a sibling check - return i === al ? - siblingCheck( a, bp[i], -1 ) : - siblingCheck( ap[i], b, 1 ); - }; - -// Always assume the presence of duplicates if sort doesn't -// pass them to our comparison function (as in Google Chrome). -[0, 0].sort( sortOrder ); -baseHasDuplicate = !hasDuplicate; - -// Document sorting and removing duplicates -Sizzle.uniqueSort = function( results ) { - var elem, - i = 1; - - hasDuplicate = baseHasDuplicate; - results.sort( sortOrder ); - - if ( hasDuplicate ) { - for ( ; (elem = results[i]); i++ ) { - if ( elem === results[ i - 1 ] ) { - results.splice( i--, 1 ); - } - } - } - - return results; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -function tokenize( selector, parseOnly ) { - var matched, match, tokens, type, soFar, groups, preFilters, - cached = tokenCache[ expando ][ 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 ) { - soFar = soFar.slice( match[0].length ); - } - groups.push( tokens = [] ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - tokens.push( matched = new Token( match.shift() ) ); - soFar = soFar.slice( matched.length ); - - // Cast descendant combinators to space - matched.type = match[0].replace( rtrim, " " ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - // The last two arguments here are (context, xml) for backCompat - (match = preFilters[ type ]( match, document, true ))) ) { - - tokens.push( matched = new Token( match.shift() ) ); - soFar = soFar.slice( matched.length ); - matched.type = type; - matched.matches = match; - } - } - - 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 addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && combinator.dir === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( checkNonElements || elem.nodeType === 1 ) { - return matcher( elem, context, xml ); - } - } - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if ( !xml ) { - var cache, - dirkey = dirruns + " " + doneName + " ", - cachedkey = dirkey + cachedruns; - while ( (elem = elem[ dir ]) ) { - if ( checkNonElements || elem.nodeType === 1 ) { - if ( (cache = elem[ expando ]) === cachedkey ) { - return elem.sizset; - } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { - if ( elem.sizset ) { - return elem; - } - } else { - elem[ expando ] = cachedkey; - if ( matcher( elem, context, xml ) ) { - elem.sizset = true; - return elem; - } - elem.sizset = false; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( checkNonElements || elem.nodeType === 1 ) { - if ( matcher( elem, context, xml ) ) { - return elem; - } - } - } - } - }; -} - -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 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 ) { - // Positional selectors apply to seed elements, so it is invalid to follow them with relative ones - if ( seed && postFinder ) { - return; - } - - var i, elem, postFilterIn, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [], seed ), - - // 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 ) { - postFilterIn = condense( matcherOut, postMap ); - postFilter( postFilterIn, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = postFilterIn.length; - while ( i-- ) { - if ( (elem = postFilterIn[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - // Keep seed and results synchronized - if ( seed ) { - // Ignore postFinder because it can't coexist with seed - i = preFilter && matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - seed[ preMap[i] ] = !(results[ preMap[i] ] = elem); - } - } - } 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.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; - } else { - // The concatenated values are (context, xml) for backCompat - 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 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && tokens.join("") - ); - } - 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, expandContext ) { - var elem, j, matcher, - setMatched = [], - matchedCount = 0, - i = "0", - unmatched = seed && [], - outermost = expandContext != null, - contextBackup = outermostContext, - // We must always have either seed elements or context - elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), - // Nested matchers should use non-integer dirruns - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); - - if ( outermost ) { - outermostContext = context !== document && context; - cachedruns = superMatcher.el; - } - - // Add elements passing elementMatchers directly to results - for ( ; (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - for ( j = 0; (matcher = elementMatchers[j]); j++ ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - cachedruns = ++superMatcher.el; - } - } - - // 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 ); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - for ( j = 0; (matcher = setMatchers[j]); 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; - }; - - superMatcher.el = 0; - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ expando ][ selector ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !group ) { - group = tokenize( selector ); - } - i = group.length; - while ( i-- ) { - cached = matcherFromTokens( group[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - } - return cached; -}; - -function multipleContexts( selector, contexts, results, seed ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results, seed ); - } - return results; -} - -function select( selector, context, results, seed, xml ) { - var i, tokens, token, type, find, - match = tokenize( selector ), - j = match.length; - - if ( !seed ) { - // Try to minimize operations if there is only one group - if ( match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - context.nodeType === 9 && !xml && - Expr.relative[ tokens[1].type ] ) { - - context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0]; - if ( !context ) { - return results; - } - - selector = selector.slice( tokens.shift().length ); - } - - // Fetch a seed set for right-to-left matching - for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; 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( rbackslash, "" ), - rsibling.test( tokens[0].type ) && context.parentNode || context, - xml - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && tokens.join(""); - if ( !selector ) { - push.apply( results, slice.call( seed, 0 ) ); - return results; - } - - break; - } - } - } - } - } - - // Compile and execute a filtering function - // Provide `match` to avoid retokenization if we modified the selector above - compile( selector, match )( - seed, - context, - xml, - results, - rsibling.test( selector ) - ); - return results; -} - -if ( document.querySelectorAll ) { - (function() { - var disconnectedMatch, - oldSelect = select, - rescape = /'|\\/g, - rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, - - // qSa(:focus) reports false when true (Chrome 21), - // A support test would require too much code (would include document ready) - rbuggyQSA = [":focus"], - - // matchesSelector(:focus) reports false when true (Chrome 21), - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - // A support test would require too much code (would include document ready) - // just skip matchesSelector for :active - rbuggyMatches = [ ":active", ":focus" ], - matches = docElem.matchesSelector || - docElem.mozMatchesSelector || - docElem.webkitMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector; - - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explictly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; - - // IE8 - Some boolean attributes are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here (do not put tests after this one) - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - - // Opera 10-12/IE9 - ^= $= *= and empty values - // Should not select anything - div.innerHTML = "

"; - if ( div.querySelectorAll("[test^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here (do not put tests after this one) - div.innerHTML = ""; - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push(":enabled", ":disabled"); - } - }); - - // rbuggyQSA always contains :focus, so no need for a length check - rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); - - select = function( selector, context, results, seed, xml ) { - // Only use querySelectorAll when not filtering, - // when this is not xml, - // and when no QSA bugs apply - if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - var groups, i, - old = true, - nid = expando, - newContext = context, - newSelector = context.nodeType === 9 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + groups[i].join(""); - } - newContext = rsibling.test( selector ) && context.parentNode || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, slice.call( newContext.querySelectorAll( - newSelector - ), 0 ) ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - - return oldSelect( selector, context, results, seed, xml ); - }; - - if ( matches ) { - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - try { - matches.call( div, "[test!='']:sizzle" ); - rbuggyMatches.push( "!=", pseudos ); - } catch ( e ) {} - }); - - // rbuggyMatches always contains :active and :focus, so no need for a length check - rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); - - Sizzle.matchesSelector = function( elem, expr ) { - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - // rbuggyMatches always contains :active, so no need for an existence check - if ( !isXML( elem ) && !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 || 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, null, null, [ elem ] ).length > 0; - }; - } - })(); -} - -// Deprecated -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Back-compat -function setFilters() {} -Expr.filters = setFilters.prototype = Expr.pseudos; -Expr.setFilters = new setFilters(); - -// Override sizzle attribute retrieval -Sizzle.attr = jQuery.attr; -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - -})( window ); -var runtil = /Until$/, - rparentsprev = /^(?:parents|prev(?:Until|All))/, - isSimple = /^.[^:#\[\.,]*$/, - rneedsContext = jQuery.expr.match.needsContext, - // 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({ - find: function( selector ) { - var i, l, length, n, r, ret, - self = this; - - if ( typeof selector !== "string" ) { - return jQuery( selector ).filter(function() { - for ( i = 0, l = self.length; i < l; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }); - } - - ret = this.pushStack( "", "find", selector ); - - for ( i = 0, l = this.length; i < l; i++ ) { - length = ret.length; - jQuery.find( selector, this[i], ret ); - - if ( i > 0 ) { - // Make sure that the results are unique - for ( n = length; n < ret.length; n++ ) { - for ( r = 0; r < length; r++ ) { - if ( ret[r] === ret[n] ) { - ret.splice(n--, 1); - break; - } - } - } - } - } - - return ret; - }, - - has: function( target ) { - var i, - targets = jQuery( target, this ), - len = targets.length; - - return this.filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector, false), "not", selector); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector, true), "filter", selector ); - }, - - is: function( selector ) { - return !!selector && ( - typeof selector === "string" ? - // 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". - rneedsContext.test( selector ) ? - jQuery( selector, this.context ).index( this[0] ) >= 0 : - jQuery.filter( selector, this ).length > 0 : - this.filter( selector ).length > 0 ); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - ret = [], - pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; - - for ( ; i < l; i++ ) { - cur = this[i]; - - while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) { - if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { - ret.push( cur ); - break; - } - cur = cur.parentNode; - } - } - - ret = ret.length > 1 ? jQuery.unique( ret ) : ret; - - return this.pushStack( ret, "closest", selectors ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return jQuery.inArray( this[0], jQuery( elem ) ); - } - - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - jQuery( selector, context ) : - jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), - all = jQuery.merge( this.get(), set ); - - return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? - all : - jQuery.unique( all ) ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) - ); - } -}); - -jQuery.fn.andSelf = jQuery.fn.addBack; - -// A painfully simple check to see if an element is disconnected -// from a document (should be improved, where feasible). -function isDisconnected( node ) { - return !node || !node.parentNode || node.parentNode.nodeType === 11; -} - -function sibling( cur, dir ) { - do { - cur = cur[ dir ]; - } while ( cur && 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 jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); - - if ( !runtil.test( name ) ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); - } - - ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; - - if ( this.length > 1 && rparentsprev.test( name ) ) { - ret = ret.reverse(); - } - - return this.pushStack( ret, name, core_slice.call( arguments ).join(",") ); - }; -}); - -jQuery.extend({ - filter: function( expr, elems, not ) { - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 ? - jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : - jQuery.find.matches(expr, elems); - }, - - dir: function( elem, dir, until ) { - var matched = [], - cur = elem[ dir ]; - - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, keep ) { - - // Can't pass null or undefined to indexOf in Firefox 4 - // Set to 0 to skip string check - qualifier = qualifier || 0; - - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep(elements, function( elem, i ) { - var retVal = !!qualifier.call( elem, i, elem ); - return retVal === keep; - }); - - } else if ( qualifier.nodeType ) { - return jQuery.grep(elements, function( elem, i ) { - return ( elem === qualifier ) === keep; - }); - - } else if ( typeof qualifier === "string" ) { - var filtered = jQuery.grep(elements, function( elem ) { - return elem.nodeType === 1; - }); - - if ( isSimple.test( qualifier ) ) { - return jQuery.filter(qualifier, filtered, !keep); - } else { - qualifier = jQuery.filter( qualifier, filtered ); - } - } - - return jQuery.grep(elements, function( elem, i ) { - return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; - }); -} -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); - - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } - } - return safeFrag; -} - -var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + - "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", - rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rtbody = /]", "i"), - rcheckableType = /^(?:checkbox|radio)$/, - // checked="checked" or checked - rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, - rscriptType = /\/(java|ecma)script/i, - rcleanScript = /^\s*\s*$/g, - wrapMap = { - option: [ 1, "" ], - legend: [ 1, "
", "
" ], - thead: [ 1, "", "
" ], - tr: [ 2, "", "
" ], - td: [ 3, "", "
" ], - col: [ 2, "", "
" ], - area: [ 1, "", "" ], - _default: [ 0, "", "" ] - }, - safeFragment = createSafeFragment( document ), - fragmentDiv = safeFragment.appendChild( document.createElement("div") ); - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, -// unless wrapped in a div with non-breaking characters in front of it. -if ( !jQuery.support.htmlSerialize ) { - wrapMap._default = [ 1, "X
", "
" ]; -} - -jQuery.fn.extend({ - text: function( value ) { - return jQuery.access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); - }, null, value, arguments.length ); - }, - - wrapAll: function( html ) { - if ( jQuery.isFunction( html ) ) { - return this.each(function(i) { - jQuery(this).wrapAll( html.call(this, i) ); - }); - } - - if ( this[0] ) { - // The elements to wrap the target around - var 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.firstChild && elem.firstChild.nodeType === 1 ) { - elem = elem.firstChild; - } - - return elem; - }).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( jQuery.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 isFunction = jQuery.isFunction( html ); - - return this.each(function(i) { - jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); - }); - }, - - unwrap: function() { - return this.parent().each(function() { - if ( !jQuery.nodeName( this, "body" ) ) { - jQuery( this ).replaceWith( this.childNodes ); - } - }).end(); - }, - - append: function() { - return this.domManip(arguments, true, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 ) { - this.appendChild( elem ); - } - }); - }, - - prepend: function() { - return this.domManip(arguments, true, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 ) { - this.insertBefore( elem, this.firstChild ); - } - }); - }, - - before: function() { - if ( !isDisconnected( this[0] ) ) { - return this.domManip(arguments, false, function( elem ) { - this.parentNode.insertBefore( elem, this ); - }); - } - - if ( arguments.length ) { - var set = jQuery.clean( arguments ); - return this.pushStack( jQuery.merge( set, this ), "before", this.selector ); - } - }, - - after: function() { - if ( !isDisconnected( this[0] ) ) { - return this.domManip(arguments, false, function( elem ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - }); - } - - if ( arguments.length ) { - var set = jQuery.clean( arguments ); - return this.pushStack( jQuery.merge( this, set ), "after", this.selector ); - } - }, - - // keepData is for internal use only--do not document - remove: function( selector, keepData ) { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( elem.getElementsByTagName("*") ); - jQuery.cleanData( [ elem ] ); - } - - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); - } - } - } - - return this; - }, - - empty: function() { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( elem.getElementsByTagName("*") ); - } - - // Remove any remaining nodes - while ( elem.firstChild ) { - elem.removeChild( elem.firstChild ); - } - } - - 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 jQuery.access( this, function( value ) { - var elem = this[0] || {}, - i = 0, - l = this.length; - - if ( value === undefined ) { - return elem.nodeType === 1 ? - elem.innerHTML.replace( rinlinejQuery, "" ) : - undefined; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && - ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && - !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { - - value = value.replace( rxhtmlTag, "<$1>" ); - - try { - for (; i < l; i++ ) { - // Remove element nodes and prevent memory leaks - elem = this[i] || {}; - if ( elem.nodeType === 1 ) { - jQuery.cleanData( elem.getElementsByTagName( "*" ) ); - 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( value ) { - if ( !isDisconnected( this[0] ) ) { - // Make sure that the elements are removed from the DOM before they are inserted - // this can help fix replacing a parent with child elements - if ( jQuery.isFunction( value ) ) { - return this.each(function(i) { - var self = jQuery(this), old = self.html(); - self.replaceWith( value.call( this, i, old ) ); - }); - } - - if ( typeof value !== "string" ) { - value = jQuery( value ).detach(); - } - - return this.each(function() { - var next = this.nextSibling, - parent = this.parentNode; - - jQuery( this ).remove(); - - if ( next ) { - jQuery(next).before( value ); - } else { - jQuery(parent).append( value ); - } - }); - } - - return this.length ? - this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : - this; - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, table, callback ) { - - // Flatten any nested arrays - args = [].concat.apply( [], args ); - - var results, first, fragment, iNoClone, - i = 0, - value = args[0], - scripts = [], - l = this.length; - - // We can't cloneNode fragments that contain checked, in WebKit - if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) { - return this.each(function() { - jQuery(this).domManip( args, table, callback ); - }); - } - - if ( jQuery.isFunction(value) ) { - return this.each(function(i) { - var self = jQuery(this); - args[0] = value.call( this, i, table ? self.html() : undefined ); - self.domManip( args, table, callback ); - }); - } - - if ( this[0] ) { - results = jQuery.buildFragment( args, this, scripts ); - fragment = results.fragment; - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - if ( first ) { - table = table && jQuery.nodeName( first, "tr" ); - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - // Fragments from the fragment cache must always be cloned and never used in place. - for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) { - callback.call( - table && jQuery.nodeName( this[i], "table" ) ? - findOrAppend( this[i], "tbody" ) : - this[i], - i === iNoClone ? - fragment : - jQuery.clone( fragment, true, true ) - ); - } - } - - // Fix #11809: Avoid leaking memory - fragment = first = null; - - if ( scripts.length ) { - jQuery.each( scripts, function( i, elem ) { - if ( elem.src ) { - if ( jQuery.ajax ) { - jQuery.ajax({ - url: elem.src, - type: "GET", - dataType: "script", - async: false, - global: false, - "throws": true - }); - } else { - jQuery.error("no ajax"); - } - } else { - jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) ); - } - - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); - } - }); - } - } - - return this; - } -}); - -function findOrAppend( elem, tag ) { - return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) ); -} - -function cloneCopyEvent( src, dest ) { - - if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { - return; - } - - var type, i, l, - oldData = jQuery._data( src ), - curData = jQuery._data( dest, oldData ), - events = oldData.events; - - if ( events ) { - delete curData.handle; - curData.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - - // make the cloned public data object a copy from the original - if ( curData.data ) { - curData.data = jQuery.extend( {}, curData.data ); - } -} - -function cloneFixAttributes( src, dest ) { - var nodeName; - - // We do not need to do anything for non-Elements - if ( dest.nodeType !== 1 ) { - return; - } - - // clearAttributes removes the attributes, which we don't want, - // but also removes the attachEvent events, which we *do* want - if ( dest.clearAttributes ) { - dest.clearAttributes(); - } - - // mergeAttributes, in contrast, only merges back on the - // original attributes, not the events - if ( dest.mergeAttributes ) { - dest.mergeAttributes( src ); - } - - nodeName = dest.nodeName.toLowerCase(); - - if ( nodeName === "object" ) { - // IE6-10 improperly clones children of object elements using classid. - // IE10 throws NoModificationAllowedError if parent is null, #12132. - if ( dest.parentNode ) { - dest.outerHTML = src.outerHTML; - } - - // This path appears unavoidable for IE9. When cloning an object - // element in IE9, the outerHTML strategy above is not sufficient. - // If the src has innerHTML and the destination does not, - // copy the src.innerHTML into the dest.innerHTML. #10324 - if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) { - dest.innerHTML = src.innerHTML; - } - - } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - // IE6-8 fails to persist the checked state of a cloned checkbox - // or radio button. Worse, IE6-7 fail to give the cloned element - // a checked appearance if the defaultChecked value isn't also set - - dest.defaultChecked = dest.checked = src.checked; - - // IE6-7 get confused and end up setting the value of a cloned - // checkbox/radio button to an empty string instead of "on" - if ( dest.value !== src.value ) { - dest.value = src.value; - } - - // IE6-8 fails to return the selected option to the default selected - // state when cloning options - } else if ( nodeName === "option" ) { - dest.selected = src.defaultSelected; - - // IE6-8 fails to set the defaultValue to the correct value when - // cloning other types of input fields - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - - // IE blanks contents when cloning scripts - } else if ( nodeName === "script" && dest.text !== src.text ) { - dest.text = src.text; - } - - // Event data gets referenced instead of copied if the expando - // gets copied too - dest.removeAttribute( jQuery.expando ); -} - -jQuery.buildFragment = function( args, context, scripts ) { - var fragment, cacheable, cachehit, - first = args[ 0 ]; - - // Set context from what may come in as undefined or a jQuery collection or a node - // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 & - // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception - context = context || document; - context = !context.nodeType && context[0] || context; - context = context.ownerDocument || context; - - // Only cache "small" (1/2 KB) HTML strings that are associated with the main document - // Cloning options loses the selected state, so don't cache them - // IE 6 doesn't like it when you put or elements in a fragment - // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache - // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501 - if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document && - first.charAt(0) === "<" && !rnocache.test( first ) && - (jQuery.support.checkClone || !rchecked.test( first )) && - (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) { - - // Mark cacheable and look for a hit - cacheable = true; - fragment = jQuery.fragments[ first ]; - cachehit = fragment !== undefined; - } - - if ( !fragment ) { - fragment = context.createDocumentFragment(); - jQuery.clean( args, context, fragment, scripts ); - - // Update the cache, but only store false - // unless this is a second parsing of the same content - if ( cacheable ) { - jQuery.fragments[ first ] = cachehit && fragment; - } - } - - return { fragment: fragment, cacheable: cacheable }; -}; - -jQuery.fragments = {}; - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - i = 0, - ret = [], - insert = jQuery( selector ), - l = insert.length, - parent = this.length === 1 && this[0].parentNode; - - if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) { - insert[ original ]( this[0] ); - return this; - } else { - for ( ; i < l; i++ ) { - elems = ( i > 0 ? this.clone(true) : this ).get(); - jQuery( insert[i] )[ original ]( elems ); - ret = ret.concat( elems ); - } - - return this.pushStack( ret, name, insert.selector ); - } - }; -}); - -function getAll( elem ) { - if ( typeof elem.getElementsByTagName !== "undefined" ) { - return elem.getElementsByTagName( "*" ); - - } else if ( typeof elem.querySelectorAll !== "undefined" ) { - return elem.querySelectorAll( "*" ); - - } else { - return []; - } -} - -// Used in clean, fixes the defaultChecked property -function fixDefaultChecked( elem ) { - if ( rcheckableType.test( elem.type ) ) { - elem.defaultChecked = elem.checked; - } -} - -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var srcElements, - destElements, - i, - clone; - - if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { - clone = elem.cloneNode( true ); - - // IE<=8 does not properly clone detached, unknown element nodes - } else { - fragmentDiv.innerHTML = elem.outerHTML; - fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); - } - - if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && - (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { - // IE copies events bound via attachEvent when using cloneNode. - // Calling detachEvent on the clone will also remove the events - // from the original. In order to get around this, we use some - // proprietary methods to clear the events. Thanks to MooTools - // guys for this hotness. - - cloneFixAttributes( elem, clone ); - - // Using Sizzle here is crazy slow, so we use getElementsByTagName instead - srcElements = getAll( elem ); - destElements = getAll( clone ); - - // Weird iteration because IE will replace the length property - // with an element if you are cloning the body and one of the - // elements on the page has a name or id of "length" - for ( i = 0; srcElements[i]; ++i ) { - // Ensure that the destination node is not null; Fixes #9587 - if ( destElements[i] ) { - cloneFixAttributes( srcElements[i], destElements[i] ); - } - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - cloneCopyEvent( elem, clone ); - - if ( deepDataAndEvents ) { - srcElements = getAll( elem ); - destElements = getAll( clone ); - - for ( i = 0; srcElements[i]; ++i ) { - cloneCopyEvent( srcElements[i], destElements[i] ); - } - } - } - - srcElements = destElements = null; - - // Return the cloned set - return clone; - }, - - clean: function( elems, context, fragment, scripts ) { - var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, - safe = context === document && safeFragment, - ret = []; - - // Ensure that context is a document - if ( !context || typeof context.createDocumentFragment === "undefined" ) { - context = document; - } - - // Use the already-created safe fragment if context permits - for ( i = 0; (elem = elems[i]) != null; i++ ) { - if ( typeof elem === "number" ) { - elem += ""; - } - - if ( !elem ) { - continue; - } - - // Convert html string into DOM nodes - if ( typeof elem === "string" ) { - if ( !rhtml.test( elem ) ) { - elem = context.createTextNode( elem ); - } else { - // Ensure a safe container in which to render the html - safe = safe || createSafeFragment( context ); - div = context.createElement("div"); - safe.appendChild( div ); - - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(rxhtmlTag, "<$1>"); - - // Go to html and back, then peel off extra wrappers - tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - depth = wrap[0]; - div.innerHTML = wrap[1] + elem + wrap[2]; - - // Move to the right depth - while ( depth-- ) { - div = div.lastChild; - } - - // Remove IE's autoinserted from table fragments - if ( !jQuery.support.tbody ) { - - // String was a , *may* have spurious - hasBody = rtbody.test(elem); - tbody = tag === "table" && !hasBody ? - div.firstChild && div.firstChild.childNodes : - - // String was a bare or - wrap[1] === "
" && !hasBody ? - div.childNodes : - []; - - for ( j = tbody.length - 1; j >= 0 ; --j ) { - if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { - tbody[ j ].parentNode.removeChild( tbody[ j ] ); - } - } - } - - // IE completely kills leading whitespace when innerHTML is used - if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { - div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); - } - - elem = div.childNodes; - - // Take out of fragment container (we need a fresh div each time) - div.parentNode.removeChild( div ); - } - } - - if ( elem.nodeType ) { - ret.push( elem ); - } else { - jQuery.merge( ret, elem ); - } - } - - // Fix #11356: Clear elements from safeFragment - if ( div ) { - elem = div = safe = null; - } - - // Reset defaultChecked for any radios and checkboxes - // about to be appended to the DOM in IE 6/7 (#8060) - if ( !jQuery.support.appendChecked ) { - for ( i = 0; (elem = ret[i]) != null; i++ ) { - if ( jQuery.nodeName( elem, "input" ) ) { - fixDefaultChecked( elem ); - } else if ( typeof elem.getElementsByTagName !== "undefined" ) { - jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked ); - } - } - } - - // Append elements to a provided document fragment - if ( fragment ) { - // Special handling of each script element - handleScript = function( elem ) { - // Check if we consider it executable - if ( !elem.type || rscriptType.test( elem.type ) ) { - // Detach the script and store it in the scripts array (if provided) or the fragment - // Return truthy to indicate that it has been handled - return scripts ? - scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) : - fragment.appendChild( elem ); - } - }; - - for ( i = 0; (elem = ret[i]) != null; i++ ) { - // Check if we're done after handling an executable script - if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) { - // Append to fragment and handle embedded scripts - fragment.appendChild( elem ); - if ( typeof elem.getElementsByTagName !== "undefined" ) { - // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration - jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript ); - - // Splice the scripts into ret after their former ancestor and advance our index beyond them - ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); - i += jsTags.length; - } - } - } - } - - return ret; - }, - - cleanData: function( elems, /* internal */ acceptData ) { - var data, id, elem, type, - i = 0, - internalKey = jQuery.expando, - cache = jQuery.cache, - deleteExpando = jQuery.support.deleteExpando, - special = jQuery.event.special; - - for ( ; (elem = elems[i]) != null; i++ ) { - - if ( acceptData || jQuery.acceptData( elem ) ) { - - id = elem[ internalKey ]; - data = id && cache[ id ]; - - if ( data ) { - 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 ); - } - } - } - - // Remove cache only if it was not already removed by jQuery.event.remove - if ( cache[ id ] ) { - - delete cache[ id ]; - - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( deleteExpando ) { - delete elem[ internalKey ]; - - } else if ( elem.removeAttribute ) { - elem.removeAttribute( internalKey ); - - } else { - elem[ internalKey ] = null; - } - - jQuery.deletedIds.push( id ); - } - } - } - } - } -}); -// Limit scope pollution from any deprecated API -(function() { - -var matched, browser; - -// Use of jQuery.browser is frowned upon. -// More details: http://api.jquery.com/jQuery.browser -// jQuery.uaMatch maintained for back-compat -jQuery.uaMatch = function( ua ) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || - /(webkit)[ \/]([\w.]+)/.exec( ua ) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || - /(msie) ([\w.]+)/.exec( ua ) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; -}; - -matched = jQuery.uaMatch( navigator.userAgent ); -browser = {}; - -if ( matched.browser ) { - browser[ matched.browser ] = true; - browser.version = matched.version; -} - -// Chrome is Webkit, but Webkit is also Safari. -if ( browser.chrome ) { - browser.webkit = true; -} else if ( browser.webkit ) { - browser.safari = true; -} - -jQuery.browser = browser; - -jQuery.sub = function() { - function jQuerySub( selector, context ) { - return new jQuerySub.fn.init( selector, context ); - } - jQuery.extend( true, jQuerySub, this ); - jQuerySub.superclass = this; - jQuerySub.fn = jQuerySub.prototype = this(); - jQuerySub.fn.constructor = jQuerySub; - jQuerySub.sub = this.sub; - jQuerySub.fn.init = function init( selector, context ) { - if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { - context = jQuerySub( context ); - } - - return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); - }; - jQuerySub.fn.init.prototype = jQuerySub.fn; - var rootjQuerySub = jQuerySub(document); - return jQuerySub; -}; - -})(); -var curCSS, iframe, iframeDoc, - ralpha = /alpha\([^)]*\)/i, - ropacity = /opacity=([^)]*)/, - rposition = /^(top|right|bottom|left)$/, - // 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]).+)/, - rmargin = /^margin/, - rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), - rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), - rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), - elemdisplay = {}, - - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: 0, - fontWeight: 400 - }, - - cssExpand = [ "Top", "Right", "Bottom", "Left" ], - cssPrefixes = [ "Webkit", "O", "Moz", "ms" ], - - eventsToggle = jQuery.fn.toggle; - -// return a css property mapped to a potentially vendor prefixed property -function vendorPropName( style, name ) { - - // shortcut for names that are not vendor prefixed - if ( name in style ) { - return name; - } - - // check for vendor prefixed names - var capName = name.charAt(0).toUpperCase() + name.slice(1), - origName = name, - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in style ) { - return name; - } - } - - return origName; -} - -function isHidden( elem, el ) { - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); -} - -function showHide( elements, show ) { - var elem, display, - values = [], - index = 0, - length = elements.length; - - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - values[ index ] = jQuery._data( elem, "olddisplay" ); - if ( show ) { - // Reset the inline display of this element to learn if it is - // being hidden by cascaded rules or not - if ( !values[ index ] && elem.style.display === "none" ) { - elem.style.display = ""; - } - - // Set elements which have been overridden with display: none - // in a stylesheet to whatever the default browser style is - // for such an element - if ( elem.style.display === "" && isHidden( elem ) ) { - values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); - } - } else { - display = curCSS( elem, "display" ); - - if ( !values[ index ] && display !== "none" ) { - jQuery._data( elem, "olddisplay", display ); - } - } - } - - // Set the display of most of the elements in a second loop - // to avoid the constant reflow - for ( index = 0; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - if ( !show || elem.style.display === "none" || elem.style.display === "" ) { - elem.style.display = show ? values[ index ] || "" : "none"; - } - } - - return elements; -} - -jQuery.fn.extend({ - css: function( name, value ) { - return jQuery.access( this, function( elem, name, value ) { - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - }, - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state, fn2 ) { - var bool = typeof state === "boolean"; - - if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) { - return eventsToggle.apply( this, arguments ); - } - - return this.each(function() { - if ( bool ? state : isHidden( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - }); - } -}); - -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; - - } - } - } - }, - - // Exclude the following css properties to add px - cssNumber: { - "fillOpacity": true, - "fontWeight": true, - "lineHeight": true, - "opacity": 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: { - // normalize float css property - "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" - }, - - // 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 = jQuery.camelCase( name ), - style = elem.style; - - name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); - - // gets hook for the prefixed version - // followed by the unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // convert relative number strings (+= or -=) to relative numbers. #7345 - if ( type === "string" && (ret = rrelNum.exec( value )) ) { - value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); - // Fixes bug #9237 - type = "number"; - } - - // Make sure that NaN and null values aren't set. See: #7116 - if ( value == null || type === "number" && isNaN( value ) ) { - return; - } - - // If a number was passed in, add 'px' to the (except for certain CSS properties) - if ( type === "number" && !jQuery.cssNumber[ origName ] ) { - value += "px"; - } - - // 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 ) { - // Wrapped to prevent IE from throwing errors when 'invalid' values are provided - // Fixes bug #5509 - try { - style[ name ] = value; - } catch(e) {} - } - - } 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, numeric, extra ) { - var val, num, hooks, - origName = jQuery.camelCase( name ); - - // Make sure that we're working with the right name - name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); - - // gets hook for the prefixed version - // followed by the unprefixed version - 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 ); - } - - //convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Return, converting to number if forced or a qualifier was provided and val looks numeric - if ( numeric || extra !== undefined ) { - num = parseFloat( val ); - return numeric || jQuery.isNumeric( num ) ? num || 0 : val; - } - return val; - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback ) { - 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.call( elem ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; - } -}); - -// NOTE: To any future maintainer, we've window.getComputedStyle -// because jsdom on node.js will break without it. -if ( window.getComputedStyle ) { - curCSS = function( elem, name ) { - var ret, width, minWidth, maxWidth, - computed = window.getComputedStyle( elem, null ), - style = elem.style; - - if ( computed ) { - - ret = computed[ name ]; - if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right - // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels - // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values - if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret; - }; -} else if ( document.documentElement.currentStyle ) { - curCSS = function( elem, name ) { - var left, rsLeft, - ret = elem.currentStyle && elem.currentStyle[ name ], - style = elem.style; - - // Avoid setting ret to empty string here - // so we don't default to auto - if ( ret == null && style && style[ name ] ) { - ret = style[ name ]; - } - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - // but not position css attributes, as those are proportional to the parent element instead - // and we can't measure the parent instead because it might trigger a "stacking dolls" problem - if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { - - // Remember the original values - left = style.left; - rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; - - // Put in the new values to get a computed value out - if ( rsLeft ) { - elem.runtimeStyle.left = elem.currentStyle.left; - } - style.left = name === "fontSize" ? "1em" : ret; - ret = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - if ( rsLeft ) { - elem.runtimeStyle.left = rsLeft; - } - } - - return ret === "" ? "auto" : ret; - }; -} - -function setPositiveNumber( elem, value, subtract ) { - var matches = rnumsplit.exec( value ); - return matches ? - Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : - value; -} - -function augmentWidthOrHeight( elem, name, extra, isBorderBox ) { - var i = extra === ( isBorderBox ? "border" : "content" ) ? - // If we already have the right measurement, avoid augmentation - 4 : - // Otherwise initialize for horizontal or vertical properties - name === "width" ? 1 : 0, - - val = 0; - - for ( ; i < 4; i += 2 ) { - // both box models exclude margin, so add it if we want it - if ( extra === "margin" ) { - // we use jQuery.css instead of curCSS here - // because of the reliableMarginRight CSS hook! - val += jQuery.css( elem, extra + cssExpand[ i ], true ); - } - - // From this point on we use curCSS for maximum performance (relevant in animations) - if ( isBorderBox ) { - // border-box includes padding, so remove it if we want content - if ( extra === "content" ) { - val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; - } - - // at this point, extra isn't border nor margin, so remove border - if ( extra !== "margin" ) { - val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; - } - } else { - // at this point, extra isn't content, so add padding - val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; - - // at this point, extra isn't content nor padding, so add border - if ( extra !== "padding" ) { - val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; - } - } - } - - return val; -} - -function getWidthOrHeight( elem, name, extra ) { - - // Start with offset property, which is equivalent to the border-box value - var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, - valueIsBorderBox = true, - isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"; - - // some non-html elements return undefined for offsetWidth, so check for null/undefined - // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 - // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 - if ( val <= 0 || val == null ) { - // Fall back to computed then uncomputed css if necessary - val = curCSS( elem, name ); - if ( val < 0 || val == null ) { - val = elem.style[ name ]; - } - - // Computed unit is not pixels. Stop here and return. - if ( rnumnonpx.test(val) ) { - return val; - } - - // we need the check for style in case a browser which returns unreliable values - // for getComputedStyle silently falls back to the reliable elem.style - valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); - - // Normalize "", auto, and prepare for extra - val = parseFloat( val ) || 0; - } - - // use the active box-sizing model to add/subtract irrelevant styles - return ( val + - augmentWidthOrHeight( - elem, - name, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox - ) - ) + "px"; -} - - -// Try to determine the default display value of an element -function css_defaultDisplay( nodeName ) { - if ( elemdisplay[ nodeName ] ) { - return elemdisplay[ nodeName ]; - } - - var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ), - display = elem.css("display"); - elem.remove(); - - // If the simple way fails, - // get element's real default display by attaching it to a temp iframe - if ( display === "none" || display === "" ) { - // Use the already-created iframe if possible - iframe = document.body.appendChild( - iframe || jQuery.extend( document.createElement("iframe"), { - frameBorder: 0, - width: 0, - height: 0 - }) - ); - - // Create a cacheable copy of the iframe document on first call. - // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML - // document to it; WebKit & Firefox won't allow reusing the iframe document. - if ( !iframeDoc || !iframe.createElement ) { - iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; - iframeDoc.write(""); - iframeDoc.close(); - } - - elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) ); - - display = curCSS( elem, "display" ); - document.body.removeChild( iframe ); - } - - // Store the correct default display - elemdisplay[ nodeName ] = display; - - return display; -} - -jQuery.each([ "height", "width" ], function( i, name ) { - jQuery.cssHooks[ name ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - // certain elements can have dimension info if we invisibly show them - // however, it must have a current display style that would benefit from this - if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) { - return jQuery.swap( elem, cssShow, function() { - return getWidthOrHeight( elem, name, extra ); - }); - } else { - return getWidthOrHeight( elem, name, extra ); - } - } - }, - - set: function( elem, value, extra ) { - return setPositiveNumber( elem, value, extra ? - augmentWidthOrHeight( - elem, - name, - extra, - jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box" - ) : 0 - ); - } - }; -}); - -if ( !jQuery.support.opacity ) { - jQuery.cssHooks.opacity = { - get: function( elem, computed ) { - // IE uses filters for opacity - return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? - ( 0.01 * parseFloat( RegExp.$1 ) ) + "" : - computed ? "1" : ""; - }, - - set: function( elem, value ) { - var style = elem.style, - currentStyle = elem.currentStyle, - opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", - filter = currentStyle && currentStyle.filter || style.filter || ""; - - // IE has trouble with opacity if it does not have layout - // Force it by setting the zoom level - style.zoom = 1; - - // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 - if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" && - style.removeAttribute ) { - - // Setting style.filter to null, "" & " " still leave "filter:" in the cssText - // if "filter:" is present at all, clearType is disabled, we want to avoid this - // style.removeAttribute is IE Only, but so apparently is this code path... - style.removeAttribute( "filter" ); - - // if there there is no filter style applied in a css rule, we are done - if ( currentStyle && !currentStyle.filter ) { - return; - } - } - - // otherwise, set new filter values - style.filter = ralpha.test( filter ) ? - filter.replace( ralpha, opacity ) : - filter + " " + opacity; - } - }; -} - -// These hooks cannot be added until DOM ready because the support test -// for it is not run until after DOM ready -jQuery(function() { - if ( !jQuery.support.reliableMarginRight ) { - jQuery.cssHooks.marginRight = { - get: function( elem, computed ) { - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - // Work around by temporarily setting element display to inline-block - return jQuery.swap( elem, { "display": "inline-block" }, function() { - if ( computed ) { - return curCSS( elem, "marginRight" ); - } - }); - } - }; - } - - // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 - // getComputedStyle returns percent when specified for top/left/bottom/right - // rather than make the css module depend on the offset module, we just check for it here - if ( !jQuery.support.pixelPosition && jQuery.fn.position ) { - jQuery.each( [ "top", "left" ], function( i, prop ) { - jQuery.cssHooks[ prop ] = { - get: function( elem, computed ) { - if ( computed ) { - var ret = curCSS( elem, prop ); - // if curCSS returns percentage, fallback to offset - return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret; - } - } - }; - }); - } - -}); - -if ( jQuery.expr && jQuery.expr.filters ) { - jQuery.expr.filters.hidden = function( elem ) { - return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none"); - }; - - jQuery.expr.filters.visible = function( elem ) { - return !jQuery.expr.filters.hidden( elem ); - }; -} - -// 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, - - // assumes a single number if not a string - parts = typeof value === "string" ? value.split(" ") : [ value ], - expanded = {}; - - for ( i = 0; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( !rmargin.test( prefix ) ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } -}); -var r20 = /%20/g, - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, - rselectTextarea = /^(?:select|textarea)/i; - -jQuery.fn.extend({ - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map(function(){ - return this.elements ? jQuery.makeArray( this.elements ) : this; - }) - .filter(function(){ - return this.name && !this.disabled && - ( this.checked || rselectTextarea.test( this.nodeName ) || - rinput.test( this.type ) ); - }) - .map(function( i, elem ){ - var val = jQuery( this ).val(); - - return val == null ? - null : - jQuery.isArray( val ) ? - jQuery.map( val, function( val, i ){ - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - }) : - { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - }).get(); - } -}); - -//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, value ) { - // If value is a function, invoke it and return its value - value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); - s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); - }; - - // Set traditional to true for jQuery <= 1.3.2 behavior. - if ( traditional === undefined ) { - traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; - } - - // If an array was passed in, assume that it is an array of form elements. - if ( jQuery.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( "&" ).replace( r20, "+" ); -}; - -function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( jQuery.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 { - // If array item is non-scalar (array or object), encode its - // numeric index to resolve deserialization ambiguity issues. - // Note that rack (as of 1.0.0) can't currently deserialize - // nested arrays properly, and attempting to do so may cause - // a server error. Possible fixes are to modify rack's - // deserialization algorithm or to provide an option or flag - // to force array serialization to be shallow. - buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); - } - }); - - } else if ( !traditional && jQuery.type( obj ) === "object" ) { - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - // Serialize scalar item. - add( prefix, obj ); - } -} -var - // Document location - ajaxLocParts, - ajaxLocation, - - rhash = /#.*$/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL - // #7653, #8125, #8152: local protocol detection - rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - rquery = /\?/, - rscript = /)<[^<]*)*<\/script>/gi, - rts = /([?&])_=[^&]*/, - rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, - - // Keep a copy of the old load method - _load = jQuery.fn.load, - - /* 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 = ["*/"] + ["*"]; - -// #8138, IE may throw an exception when accessing -// a field from window.location if document.domain has been set -try { - ajaxLocation = location.href; -} catch( e ) { - // Use the href attribute of an A element - // since IE will modify it given document.location - ajaxLocation = document.createElement( "a" ); - ajaxLocation.href = ""; - ajaxLocation = ajaxLocation.href; -} - -// Segment location into parts -ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; - -// 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, list, placeBefore, - dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ), - i = 0, - length = dataTypes.length; - - if ( jQuery.isFunction( func ) ) { - // For each dataType in the dataTypeExpression - for ( ; i < length; i++ ) { - dataType = dataTypes[ i ]; - // We control if we're asked to add before - // any existing element - placeBefore = /^\+/.test( dataType ); - if ( placeBefore ) { - dataType = dataType.substr( 1 ) || "*"; - } - list = structure[ dataType ] = structure[ dataType ] || []; - // then we add to the structure accordingly - list[ placeBefore ? "unshift" : "push" ]( func ); - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR, - dataType /* internal */, inspected /* internal */ ) { - - dataType = dataType || options.dataTypes[ 0 ]; - inspected = inspected || {}; - - inspected[ dataType ] = true; - - var selection, - list = structure[ dataType ], - i = 0, - length = list ? list.length : 0, - executeOnly = ( structure === prefilters ); - - for ( ; i < length && ( executeOnly || !selection ); i++ ) { - selection = list[ i ]( options, originalOptions, jqXHR ); - // If we got redirected to another dataType - // we try there if executing only and not done already - if ( typeof selection === "string" ) { - if ( !executeOnly || inspected[ selection ] ) { - selection = undefined; - } else { - options.dataTypes.unshift( selection ); - selection = inspectPrefiltersOrTransports( - structure, options, originalOptions, jqXHR, selection, inspected ); - } - } - } - // If we're only executing or nothing was selected - // we try the catchall dataType if not done already - if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) { - selection = inspectPrefiltersOrTransports( - structure, options, originalOptions, jqXHR, "*", inspected ); - } - // unnecessary when only executing (prefilters) - // but it'll be ignored by the caller in that case - return selection; -} - -// 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 ); - } -} - -jQuery.fn.load = function( url, params, callback ) { - if ( typeof url !== "string" && _load ) { - return _load.apply( this, arguments ); - } - - // Don't do a request if no elements are being requested - if ( !this.length ) { - return this; - } - - var selector, type, response, - self = this, - off = url.indexOf(" "); - - if ( off >= 0 ) { - selector = url.slice( off, url.length ); - url = url.slice( 0, off ); - } - - // If it's a function - if ( jQuery.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"; - } - - // Request the remote document - jQuery.ajax({ - url: url, - - // if "type" variable is undefined, then "GET" method will be used - type: type, - dataType: "html", - data: params, - complete: function( jqXHR, status ) { - if ( callback ) { - self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] ); - } - } - }).done(function( responseText ) { - - // Save response for use in complete callback - response = arguments; - - // See if a selector was specified - self.html( selector ? - - // Create a dummy div to hold the results - jQuery("
") - - // inject the contents of the document in, removing the scripts - // to avoid any 'Permission Denied' errors in IE - .append( responseText.replace( rscript, "" ) ) - - // Locate the specified elements - .find( selector ) : - - // If not, just inject the full result - responseText ); - - }); - - return this; -}; - -// Attach a bunch of functions for handling common AJAX events -jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){ - jQuery.fn[ o ] = function( f ){ - return this.on( o, f ); - }; -}); - -jQuery.each( [ "get", "post" ], function( i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - // shift arguments if data argument was omitted - if ( jQuery.isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - return jQuery.ajax({ - type: method, - url: url, - data: data, - success: callback, - dataType: type - }); - }; -}); - -jQuery.extend({ - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - // 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 ) { - if ( settings ) { - // Building a settings object - ajaxExtend( target, jQuery.ajaxSettings ); - } else { - // Extending ajaxSettings - settings = target; - target = jQuery.ajaxSettings; - } - ajaxExtend( target, settings ); - return target; - }, - - ajaxSettings: { - url: ajaxLocation, - isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), - global: true, - type: "GET", - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - processData: true, - async: true, - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - xml: "application/xml, text/xml", - html: "text/html", - text: "text/plain", - json: "application/json, text/javascript", - "*": allTypes - }, - - contents: { - xml: /xml/, - html: /html/, - json: /json/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText" - }, - - // List of data converters - // 1) key format is "source_type destination_type" (a single space in-between) - // 2) the catchall symbol "*" can be used for source_type - converters: { - - // Convert anything to text - "* text": window.String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": jQuery.parseJSON, - - // 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: { - context: true, - url: true - } - }, - - 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 // ifModified key - ifModifiedKey, - // Response headers - responseHeadersString, - responseHeaders, - // transport - transport, - // timeout handle - timeoutTimer, - // Cross-domain detection vars - parts, - // To know if global events are to be dispatched - fireGlobals, - // Loop variable - i, - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - // Callbacks context - callbackContext = s.context || s, - // Context for global events - // It's the callbackContext if one was provided in the options - // and if it's a DOM node or a jQuery collection - globalEventContext = callbackContext !== s && - ( callbackContext.nodeType || callbackContext instanceof 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 = {}, - // The jqXHR state - state = 0, - // Default abort message - strAbort = "canceled", - // Fake xhr - jqXHR = { - - readyState: 0, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( !state ) { - var lname = name.toLowerCase(); - name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Raw string - getAllResponseHeaders: function() { - return state === 2 ? responseHeadersString : null; - }, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( state === 2 ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; - } - } - match = responseHeaders[ key.toLowerCase() ]; - } - return match === undefined ? null : match; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( !state ) { - s.mimeType = type; - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - statusText = statusText || strAbort; - if ( transport ) { - transport.abort( statusText ); - } - done( 0, statusText ); - return this; - } - }; - - // Callback for when everything is done - // It is defined here because jslint complains if it is declared - // at the end of the function (which would be more logical and readable) - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Called once - if ( state === 2 ) { - return; - } - - // State is "done" now - state = 2; - - // Clear timeout if it exists - if ( timeoutTimer ) { - 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; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // If successful, handle type chaining - if ( status >= 200 && status < 300 || status === 304 ) { - - // 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[ ifModifiedKey ] = modified; - } - modified = jqXHR.getResponseHeader("Etag"); - if ( modified ) { - jQuery.etag[ ifModifiedKey ] = modified; - } - } - - // If not modified - if ( status === 304 ) { - - statusText = "notmodified"; - isSuccess = true; - - // If we have data - } else { - - isSuccess = ajaxConvert( s, response ); - statusText = isSuccess.state; - success = isSuccess.data; - error = isSuccess.error; - isSuccess = !error; - } - } else { - // We extract error from statusText - // then normalize statusText and status for non-aborts - error = statusText; - if ( !statusText || status ) { - 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( "ajax" + ( isSuccess ? "Success" : "Error" ), - [ 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" ); - } - } - } - - // Attach deferreds - deferred.promise( jqXHR ); - jqXHR.success = jqXHR.done; - jqXHR.error = jqXHR.fail; - jqXHR.complete = completeDeferred.add; - - // Status-dependent callbacks - jqXHR.statusCode = function( map ) { - if ( map ) { - var tmp; - if ( state < 2 ) { - for ( tmp in map ) { - statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; - } - } else { - tmp = map[ jqXHR.status ]; - jqXHR.always( tmp ); - } - } - return this; - }; - - // Remove hash character (#7531: and string promotion) - // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) - // We also use the url parameter if available - s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); - - // Extract dataTypes list - s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace ); - - // A cross-domain request is in order when we have a protocol:host:port mismatch - if ( s.crossDomain == null ) { - parts = rurl.exec( s.url.toLowerCase() ) || false; - s.crossDomain = parts && ( parts.join(":") + ( parts[ 3 ] ? "" : parts[ 1 ] === "http:" ? 80 : 443 ) ) !== - ( ajaxLocParts.join(":") + ( ajaxLocParts[ 3 ] ? "" : ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ); - } - - // 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 ( state === 2 ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - fireGlobals = s.global; - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // If data is available, append data to url - if ( s.data ) { - s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; - // #9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Get ifModifiedKey before adding the anti-cache parameter - ifModifiedKey = s.url; - - // Add anti-cache in url if needed - if ( s.cache === false ) { - - var ts = jQuery.now(), - // try replacing _= if it is there - ret = s.url.replace( rts, "$1_=" + ts ); - - // if nothing was replaced, add timestamp to the end - s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); - } - } - - // 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 If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - ifModifiedKey = ifModifiedKey || s.url; - if ( jQuery.lastModified[ ifModifiedKey ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); - } - if ( jQuery.etag[ ifModifiedKey ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); - } - } - - // 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 || state === 2 ) ) { - // Abort if not done already and return - return jqXHR.abort(); - - } - - // aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - for ( i in { success: 1, error: 1, complete: 1 } ) { - jqXHR[ i ]( s[ i ] ); - } - - // 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 ] ); - } - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = setTimeout( function(){ - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - state = 1; - transport.send( requestHeaders, done ); - } catch (e) { - // Propagate exception as error if not done - if ( state < 2 ) { - done( -1, e ); - // Simply rethrow otherwise - } else { - throw e; - } - } - } - - return jqXHR; - }, - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {} - -}); - -/* Handles responses to an ajax request: - * - sets all responseXXX fields accordingly - * - 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, - responseFields = s.responseFields; - - // Fill responseXXX fields - for ( type in responseFields ) { - if ( type in responses ) { - jqXHR[ responseFields[type] ] = responses[ type ]; - } - } - - // 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 -function ajaxConvert( s, response ) { - - var conv, conv2, current, tmp, - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(), - prev = dataTypes[ 0 ], - converters = {}, - i = 0; - - // Apply the dataFilter if provided - if ( s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - // Convert to each sequential dataType, tolerating list modification - for ( ; (current = dataTypes[++i]); ) { - - // There's only work to do if current dataType is non-auto - if ( current !== "*" ) { - - // Convert response if prev dataType is non-auto and differs from current - 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.splice( i--, 0, current ); - } - - 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 }; - } - } - } - } - - // Update prev for next iteration - prev = current; - } - } - - return { state: "success", data: response }; -} -var oldCallbacks = [], - rquestion = /\?/, - rjsonp = /(=)\?(?=&|$)|\?\?/, - nonce = jQuery.now(); - -// 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, - data = s.data, - url = s.url, - hasCallback = s.jsonp !== false, - replaceInUrl = hasCallback && rjsonp.test( url ), - replaceInData = hasCallback && !replaceInUrl && typeof data === "string" && - !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && - rjsonp.test( data ); - - // Handle iff the expected data type is "jsonp" or we have a parameter to set - if ( s.dataTypes[ 0 ] === "jsonp" || replaceInUrl || replaceInData ) { - - // Get callback name, remembering preexisting value associated with it - callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? - s.jsonpCallback() : - s.jsonpCallback; - overwritten = window[ callbackName ]; - - // Insert callback into url or form data - if ( replaceInUrl ) { - s.url = url.replace( rjsonp, "$1" + callbackName ); - } else if ( replaceInData ) { - s.data = data.replace( rjsonp, "$1" + callbackName ); - } else if ( hasCallback ) { - s.url += ( rquestion.test( 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 - window[ callbackName ] = function() { - responseContainer = arguments; - }; - - // Clean-up function (fires after converters) - jqXHR.always(function() { - // Restore preexisting value - 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 && jQuery.isFunction( overwritten ) ) { - overwritten( responseContainer[ 0 ] ); - } - - responseContainer = overwritten = undefined; - }); - - // Delegate to script - return "script"; - } -}); -// Install script dataType -jQuery.ajaxSetup({ - accepts: { - script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /javascript|ecmascript/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -}); - -// Handle cache's special case and global -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - s.global = false; - } -}); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function(s) { - - // This transport only deals with cross domain requests - if ( s.crossDomain ) { - - var script, - head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement; - - return { - - send: function( _, callback ) { - - script = document.createElement( "script" ); - - script.async = "async"; - - if ( s.scriptCharset ) { - script.charset = s.scriptCharset; - } - - script.src = s.url; - - // Attach handlers for all browsers - script.onload = script.onreadystatechange = function( _, isAbort ) { - - if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { - - // Handle memory leak in IE - script.onload = script.onreadystatechange = null; - - // Remove the script - if ( head && script.parentNode ) { - head.removeChild( script ); - } - - // Dereference the script - script = undefined; - - // Callback if not abort - if ( !isAbort ) { - callback( 200, "success" ); - } - } - }; - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709 and #4378). - head.insertBefore( script, head.firstChild ); - }, - - abort: function() { - if ( script ) { - script.onload( 0, 1 ); - } - } - }; - } -}); -var xhrCallbacks, - // #5280: Internet Explorer will keep connections alive if we don't abort on unload - xhrOnUnloadAbort = window.ActiveXObject ? function() { - // Abort all pending requests - for ( var key in xhrCallbacks ) { - xhrCallbacks[ key ]( 0, 1 ); - } - } : false, - xhrId = 0; - -// Functions to create xhrs -function createStandardXHR() { - try { - return new window.XMLHttpRequest(); - } catch( e ) {} -} - -function createActiveXHR() { - try { - return new window.ActiveXObject( "Microsoft.XMLHTTP" ); - } catch( e ) {} -} - -// Create the request object -// (This is still attached to ajaxSettings for backward compatibility) -jQuery.ajaxSettings.xhr = window.ActiveXObject ? - /* Microsoft failed to properly - * implement the XMLHttpRequest in IE7 (can't request local files), - * so we use the ActiveXObject when it is available - * Additionally XMLHttpRequest can be disabled in IE7/IE8 so - * we need a fallback. - */ - function() { - return !this.isLocal && createStandardXHR() || createActiveXHR(); - } : - // For all other browsers, use the standard XMLHttpRequest object - createStandardXHR; - -// Determine support properties -(function( xhr ) { - jQuery.extend( jQuery.support, { - ajax: !!xhr, - cors: !!xhr && ( "withCredentials" in xhr ) - }); -})( jQuery.ajaxSettings.xhr() ); - -// Create transport if the browser can provide an xhr -if ( jQuery.support.ajax ) { - - jQuery.ajaxTransport(function( s ) { - // Cross domain only allowed if supported through XMLHttpRequest - if ( !s.crossDomain || jQuery.support.cors ) { - - var callback; - - return { - send: function( headers, complete ) { - - // Get a new xhr - var handle, i, - xhr = s.xhr(); - - // Open the socket - // Passing null username, generates a login popup on Opera (#2865) - if ( s.username ) { - xhr.open( s.type, s.url, s.async, s.username, s.password ); - } else { - xhr.open( s.type, s.url, s.async ); - } - - // Apply custom fields if provided - if ( s.xhrFields ) { - for ( i in s.xhrFields ) { - xhr[ i ] = s.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( s.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( s.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 ( !s.crossDomain && !headers["X-Requested-With"] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Need an extra try/catch for cross domain requests in Firefox 3 - try { - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - } catch( _ ) {} - - // Do send the request - // This may raise an exception which is actually - // handled in jQuery.ajax (so no try/catch here) - xhr.send( ( s.hasContent && s.data ) || null ); - - // Listener - callback = function( _, isAbort ) { - - var status, - statusText, - responseHeaders, - responses, - xml; - - // Firefox throws exceptions when accessing properties - // of an xhr when a network error occurred - // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) - try { - - // Was never called and is aborted or complete - if ( callback && ( isAbort || xhr.readyState === 4 ) ) { - - // Only called once - callback = undefined; - - // Do not keep as active anymore - if ( handle ) { - xhr.onreadystatechange = jQuery.noop; - if ( xhrOnUnloadAbort ) { - delete xhrCallbacks[ handle ]; - } - } - - // If it's an abort - if ( isAbort ) { - // Abort it manually if needed - if ( xhr.readyState !== 4 ) { - xhr.abort(); - } - } else { - status = xhr.status; - responseHeaders = xhr.getAllResponseHeaders(); - responses = {}; - xml = xhr.responseXML; - - // Construct response list - if ( xml && xml.documentElement /* #4958 */ ) { - responses.xml = xml; - } - - // When requesting binary data, IE6-9 will throw an exception - // on any attempt to access responseText (#11426) - try { - responses.text = xhr.responseText; - } catch( _ ) { - } - - // Firefox throws an exception when accessing - // statusText for faulty cross-domain requests - try { - statusText = xhr.statusText; - } catch( e ) { - // We normalize with Webkit giving an empty statusText - statusText = ""; - } - - // Filter status for non standard behaviors - - // If the request is local and we have data: assume a success - // (success with no data won't get notified, that's the best we - // can do given current implementations) - if ( !status && s.isLocal && !s.crossDomain ) { - status = responses.text ? 200 : 404; - // IE - #1450: sometimes returns 1223 when it should be 204 - } else if ( status === 1223 ) { - status = 204; - } - } - } - } catch( firefoxAccessException ) { - if ( !isAbort ) { - complete( -1, firefoxAccessException ); - } - } - - // Call complete if needed - if ( responses ) { - complete( status, statusText, responses, responseHeaders ); - } - }; - - if ( !s.async ) { - // if we're in sync mode we fire the callback - callback(); - } else if ( xhr.readyState === 4 ) { - // (IE6 & IE7) if it's in cache and has been - // retrieved directly we need to fire the callback - setTimeout( callback, 0 ); - } else { - handle = ++xhrId; - if ( xhrOnUnloadAbort ) { - // Create the active xhrs callbacks list if needed - // and attach the unload handler - if ( !xhrCallbacks ) { - xhrCallbacks = {}; - jQuery( window ).unload( xhrOnUnloadAbort ); - } - // Add to list of active xhrs callbacks - xhrCallbacks[ handle ] = callback; - } - xhr.onreadystatechange = callback; - } - }, - - abort: function() { - if ( callback ) { - callback(0,1); - } - } - }; - } - }); -} -var fxNow, timerId, - rfxtypes = /^(?:toggle|show|hide)$/, - rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ), - rrun = /queueHooks$/, - animationPrefilters = [ defaultPrefilter ], - tweeners = { - "*": [function( prop, value ) { - var end, unit, - tween = this.createTween( prop, value ), - parts = rfxnum.exec( value ), - target = tween.cur(), - start = +target || 0, - scale = 1, - maxIterations = 20; - - if ( parts ) { - end = +parts[2]; - unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - - // We need to compute starting value - if ( unit !== "px" && start ) { - // Iteratively approximate from a nonzero starting point - // Prefer the current property, because this process will be trivial if it uses the same units - // Fallback to end or a simple constant - start = jQuery.css( tween.elem, prop, true ) || end || 1; - - do { - // If previous iteration zeroed out, double until we get *something* - // Use a string for doubling factor so we don't accidentally see scale as unchanged below - scale = scale || ".5"; - - // Adjust and apply - start = start / scale; - jQuery.style( tween.elem, prop, start + unit ); - - // Update scale, tolerating zero or NaN from tween.cur() - // And breaking the loop if scale is unchanged or perfect, or if we've just had enough - } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); - } - - tween.unit = unit; - tween.start = start; - // If a +=/-= token was provided, we're doing a relative animation - tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end; - } - return tween; - }] - }; - -// Animations created synchronously will run synchronously -function createFxNow() { - setTimeout(function() { - fxNow = undefined; - }, 0 ); - return ( fxNow = jQuery.now() ); -} - -function createTweens( animation, props ) { - jQuery.each( props, function( prop, value ) { - var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( collection[ index ].call( animation, prop, value ) ) { - - // we're done with this property - return; - } - } - }); -} - -function Animation( elem, properties, options ) { - var result, - index = 0, - tweenerIndex = 0, - length = animationPrefilters.length, - deferred = jQuery.Deferred().always( function() { - // don't match elem in the :animated selector - delete tick.elem; - }), - tick = function() { - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - percent = 1 - ( remaining / animation.duration || 0 ), - index = 0, - length = animation.tweens.length; - - for ( ; index < length ; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ]); - - if ( percent < 1 && length ) { - return remaining; - } else { - deferred.resolveWith( elem, [ animation ] ); - return false; - } - }, - animation = deferred.promise({ - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { specialEasing: {} }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end, easing ) { - 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; - - for ( ; index < length ; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // resolve when we played the last frame - // otherwise, reject - if ( gotoEnd ) { - 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 = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - return result; - } - } - - createTweens( animation, props ); - - if ( jQuery.isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - jQuery.fx.timer( - jQuery.extend( tick, { - anim: animation, - queue: animation.opts.queue, - elem: elem - }) - ); - - // attach callbacks from options - return animation.progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); -} - -function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = jQuery.camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( jQuery.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 wont overwrite keys already present. - // also - reusing 'index' from above 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; - } - } -} - -jQuery.Animation = jQuery.extend( Animation, { - - tweener: function( props, callback ) { - if ( jQuery.isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.split(" "); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length ; index++ ) { - prop = props[ index ]; - tweeners[ prop ] = tweeners[ prop ] || []; - tweeners[ prop ].unshift( callback ); - } - }, - - prefilter: function( callback, prepend ) { - if ( prepend ) { - animationPrefilters.unshift( callback ); - } else { - animationPrefilters.push( callback ); - } - } -}); - -function defaultPrefilter( elem, props, opts ) { - var index, prop, value, length, dataShow, tween, hooks, oldfire, - anim = this, - style = elem.style, - orig = {}, - handled = [], - hidden = elem.nodeType && isHidden( elem ); - - // handle queue: false promises - 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() { - // doing this makes sure that the complete handler will be called - // before this completes - anim.always(function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - }); - }); - } - - // height/width overflow pass - if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { - // Make sure that nothing sneaks out - // Record all 3 overflow attributes because IE does not - // change the overflow attribute when overflowX and - // overflowY are set to the same value - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Set display property to inline-block for height/width - // animations on inline elements that are having width/height animated - if ( jQuery.css( elem, "display" ) === "inline" && - jQuery.css( elem, "float" ) === "none" ) { - - // inline-level elements accept inline-block; - // block-level elements need to be inline with layout - if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) { - style.display = "inline-block"; - - } else { - style.zoom = 1; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - if ( !jQuery.support.shrinkWrapBlocks ) { - anim.done(function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - }); - } - } - - - // show/hide pass - for ( index in props ) { - value = props[ index ]; - if ( rfxtypes.exec( value ) ) { - delete props[ index ]; - if ( value === ( hidden ? "hide" : "show" ) ) { - continue; - } - handled.push( index ); - } - } - - length = handled.length; - if ( length ) { - dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); - if ( hidden ) { - jQuery( elem ).show(); - } else { - anim.done(function() { - jQuery( elem ).hide(); - }); - } - anim.done(function() { - var prop; - jQuery.removeData( elem, "fxshow", true ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - }); - for ( index = 0 ; index < length ; index++ ) { - prop = handled[ index ]; - tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 ); - orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop ); - - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = tween.start; - if ( hidden ) { - tween.end = tween.start; - tween.start = prop === "width" || prop === "height" ? 1 : 0; - } - } - } - } -} - -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 || "swing"; - 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; - - if ( tween.elem[ tween.prop ] != null && - (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { - return tween.elem[ tween.prop ]; - } - - // passing any value as a 4th parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails - // so, 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, false, "" ); - // 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 its - // available and use plain properties where available - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.style && ( 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; - } - } - } -}; - -// Remove in 2.0 - this supports IE8's 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.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" || - // special check for .toggle( handler, handler, ... ) - ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; -}); - -jQuery.fn.extend({ - fadeTo: function( speed, to, easing, callback ) { - - // show any hidden elements after setting opacity to 0 - return this.filter( isHidden ).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 resolve immediately - if ( empty ) { - anim.stop( true ); - } - }; - - 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 = jQuery._data( 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 ); - } - }); - } -}); - -// Generate parameters to create a standard animation -function genFx( type, includeWidth ) { - var which, - attrs = { height: type }, - i = 0; - - // if we include width, step value is 1 to do all cssExpand values, - // if we don't include width, 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; -} - -// 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.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - jQuery.isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing - }; - - opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : - opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ 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 ( jQuery.isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; -}; - -jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p*Math.PI ) / 2; - } -}; - -jQuery.timers = []; -jQuery.fx = Tween.prototype.init; -jQuery.fx.tick = function() { - var timer, - timers = jQuery.timers, - i = 0; - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - // Checks the timer has not already been removed - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } -}; - -jQuery.fx.timer = function( timer ) { - if ( timer() && jQuery.timers.push( timer ) && !timerId ) { - timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); - } -}; - -jQuery.fx.interval = 13; - -jQuery.fx.stop = function() { - clearInterval( timerId ); - timerId = null; -}; - -jQuery.fx.speeds = { - slow: 600, - fast: 200, - // Default speed - _default: 400 -}; - -// Back Compat <1.8 extension point -jQuery.fx.step = {}; - -if ( jQuery.expr && jQuery.expr.filters ) { - jQuery.expr.filters.animated = function( elem ) { - return jQuery.grep(jQuery.timers, function( fn ) { - return elem === fn.elem; - }).length; - }; -} -var rroot = /^(?:body|html)$/i; - -jQuery.fn.offset = function( options ) { - if ( arguments.length ) { - return options === undefined ? - this : - this.each(function( i ) { - jQuery.offset.setOffset( this, options, i ); - }); - } - - var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, - box = { top: 0, left: 0 }, - elem = this[ 0 ], - doc = elem && elem.ownerDocument; - - if ( !doc ) { - return; - } - - if ( (body = doc.body) === elem ) { - return jQuery.offset.bodyOffset( elem ); - } - - docElem = doc.documentElement; - - // Make sure it's not a disconnected DOM node - if ( !jQuery.contains( docElem, elem ) ) { - return box; - } - - // If we don't have gBCR, just use 0,0 rather than error - // BlackBerry 5, iOS 3 (original iPhone) - if ( typeof elem.getBoundingClientRect !== "undefined" ) { - box = elem.getBoundingClientRect(); - } - win = getWindow( doc ); - clientTop = docElem.clientTop || body.clientTop || 0; - clientLeft = docElem.clientLeft || body.clientLeft || 0; - scrollTop = win.pageYOffset || docElem.scrollTop; - scrollLeft = win.pageXOffset || docElem.scrollLeft; - return { - top: box.top + scrollTop - clientTop, - left: box.left + scrollLeft - clientLeft - }; -}; - -jQuery.offset = { - - bodyOffset: function( body ) { - var top = body.offsetTop, - left = body.offsetLeft; - - if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) { - top += parseFloat( jQuery.css(body, "marginTop") ) || 0; - left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; - } - - return { top: top, left: left }; - }, - - setOffset: function( elem, options, i ) { - var position = jQuery.css( elem, "position" ); - - // set position first, in-case top/left are set even on static elem - if ( position === "static" ) { - elem.style.position = "relative"; - } - - var curElem = jQuery( elem ), - curOffset = curElem.offset(), - curCSSTop = jQuery.css( elem, "top" ), - curCSSLeft = jQuery.css( elem, "left" ), - calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, - props = {}, curPosition = {}, curTop, curLeft; - - // 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 ( jQuery.isFunction( options ) ) { - options = options.call( elem, i, 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({ - - position: function() { - if ( !this[0] ) { - return; - } - - var elem = this[0], - - // Get *real* offsetParent - offsetParent = this.offsetParent(), - - // Get correct offsets - offset = this.offset(), - parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); - - // Subtract element margins - // note: when an element has margin: auto the offsetLeft and marginLeft - // are the same in Safari causing offset.left to incorrectly be 0 - offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; - offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; - - // Add offsetParent borders - parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; - parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; - - // Subtract the two offsets - return { - top: offset.top - parentOffset.top, - left: offset.left - parentOffset.left - }; - }, - - offsetParent: function() { - return this.map(function() { - var offsetParent = this.offsetParent || document.body; - while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { - offsetParent = offsetParent.offsetParent; - } - return offsetParent || document.body; - }); - } -}); - - -// Create scrollLeft and scrollTop methods -jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { - var top = /Y/.test( prop ); - - jQuery.fn[ method ] = function( val ) { - return jQuery.access( this, function( elem, method, val ) { - var win = getWindow( elem ); - - if ( val === undefined ) { - return win ? (prop in win) ? win[ prop ] : - win.document.documentElement[ method ] : - elem[ method ]; - } - - if ( win ) { - win.scrollTo( - !top ? val : jQuery( win ).scrollLeft(), - top ? val : jQuery( win ).scrollTop() - ); - - } else { - elem[ method ] = val; - } - }, method, val, arguments.length, null ); - }; -}); - -function getWindow( elem ) { - return jQuery.isWindow( elem ) ? - elem : - elem.nodeType === 9 ? - elem.defaultView || elem.parentWindow : - false; -} -// 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 jQuery.access( this, function( elem, type, value ) { - var doc; - - if ( jQuery.isWindow( elem ) ) { - // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there - // isn't a whole lot we can do. See pull request at this URL for discussion: - // https://github.com/jquery/jquery/pull/764 - return 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 - // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it. - 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, value, extra ) : - - // Set width or height on the element - jQuery.style( elem, type, value, extra ); - }, type, chainable ? margin : undefined, chainable, null ); - }; - }); -}); -// Expose jQuery to the global object -window.jQuery = window.$ = jQuery; - -// Expose jQuery as an AMD module, but only for AMD loaders that -// understand the issues with loading multiple versions of jQuery -// in a page that all might call define(). The loader will indicate -// they have special allowances for multiple jQuery versions by -// specifying define.amd.jQuery = true. Register as a named module, -// since jQuery can be concatenated with other files that may use define, -// but not use 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. -if ( typeof define === "function" && define.amd && define.amd.jQuery ) { - define( "jquery", [], function () { return jQuery; } ); -} - -})( window ); - -/** - * @license AngularJS v1.1.5 - * (c) 2010-2012 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, document){ - var _jQuery = window.jQuery.noConflict(true); - -//////////////////////////////////// - -/** - * @ngdoc function - * @name angular.lowercase - * @function - * - * @description Converts the specified string to lowercase. - * @param {string} string String to be converted to lowercase. - * @returns {string} Lowercased string. - */ -var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;}; - - -/** - * @ngdoc function - * @name angular.uppercase - * @function - * - * @description Converts the specified string to uppercase. - * @param {string} string String to be converted to uppercase. - * @returns {string} Uppercased string. - */ -var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;}; - - -var manualLowercase = function(s) { - return isString(s) - ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) - : s; -}; -var manualUppercase = function(s) { - return isString(s) - ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) - : s; -}; - - -// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish -// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods -// with correct but slower alternatives. -if ('i' !== 'I'.toLowerCase()) { - lowercase = manualLowercase; - uppercase = manualUppercase; -} - - -var /** holds major version number for IE or NaN for real browsers */ - msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]), - jqLite, // delay binding since jQuery could be loaded after us. - jQuery, // delay binding - slice = [].slice, - push = [].push, - toString = Object.prototype.toString, - - - _angular = window.angular, - /** @name angular */ - angular = window.angular || (window.angular = {}), - angularModule, - nodeName_, - uid = ['0', '0', '0']; - -/** - * @ngdoc function - * @name angular.noConflict - * @function - * - * @description - * Restores the previous global value of angular and returns the current instance. Other libraries may already use the - * angular namespace. Or a previous version of angular is already loaded on the page. In these cases you may want to - * restore the previous namespace and keep a reference to angular. - * - * @return {Object} The current angular namespace - */ -function noConflict() { - var a = window.angular; - window.angular = _angular; - return a; -} - -/** - * @private - * @param {*} obj - * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...) - */ -function isArrayLike(obj) { - if (!obj || (typeof obj.length !== 'number')) return false; - - // We have on object which has length property. Should we treat it as array? - if (typeof obj.hasOwnProperty != 'function' && - typeof obj.constructor != 'function') { - // This is here for IE8: it is a bogus object treat it as array; - return true; - } else { - return obj instanceof JQLite || // JQLite - (jQuery && obj instanceof jQuery) || // jQuery - toString.call(obj) !== '[object Object]' || // some browser native object - typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj) - } -} - -/** - * @ngdoc function - * @name angular.forEach - * @function - * - * @description - * Invokes the `iterator` function once for each item in `obj` collection, which can be either an - * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` - * is the value of an object property or an array element and `key` is the object property key or - * array element index. Specifying a `context` for the function is optional. - * - * Note: this function was previously known as `angular.foreach`. - * -
-     var values = {name: 'misko', gender: 'male'};
-     var log = [];
-     angular.forEach(values, function(value, key){
-       this.push(key + ': ' + value);
-     }, log);
-     expect(log).toEqual(['name: misko', 'gender:male']);
-   
- * - * @param {Object|Array} obj Object to iterate over. - * @param {Function} iterator Iterator function. - * @param {Object=} context Object to become context (`this`) for the iterator function. - * @returns {Object|Array} Reference to `obj`. - */ -function forEach(obj, iterator, context) { - var key; - if (obj) { - if (isFunction(obj)){ - for (key in obj) { - if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key); - } - } - } else if (obj.forEach && obj.forEach !== forEach) { - obj.forEach(iterator, context); - } else if (isArrayLike(obj)) { - for (key = 0; key < obj.length; key++) - iterator.call(context, obj[key], key); - } else { - for (key in obj) { - if (obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key); - } - } - } - } - return obj; -} - -function sortedKeys(obj) { - var keys = []; - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - keys.push(key); - } - } - return keys.sort(); -} - -function forEachSorted(obj, iterator, context) { - var keys = sortedKeys(obj); - for ( var i = 0; i < keys.length; i++) { - iterator.call(context, obj[keys[i]], keys[i]); - } - return keys; -} - - -/** - * when using forEach the params are value, key, but it is often useful to have key, value. - * @param {function(string, *)} iteratorFn - * @returns {function(*, string)} - */ -function reverseParams(iteratorFn) { - return function(value, key) { iteratorFn(key, value) }; -} - -/** - * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric - * characters such as '012ABC'. The reason why we are not using simply a number counter is that - * the number string gets longer over time, and it can also overflow, where as the nextId - * will grow much slower, it is a string, and it will never overflow. - * - * @returns an unique alpha-numeric string - */ -function nextUid() { - var index = uid.length; - var digit; - - while(index) { - index--; - digit = uid[index].charCodeAt(0); - if (digit == 57 /*'9'*/) { - uid[index] = 'A'; - return uid.join(''); - } - if (digit == 90 /*'Z'*/) { - uid[index] = '0'; - } else { - uid[index] = String.fromCharCode(digit + 1); - return uid.join(''); - } - } - uid.unshift('0'); - return uid.join(''); -} - - -/** - * Set or clear the hashkey for an object. - * @param obj object - * @param h the hashkey (!truthy to delete the hashkey) - */ -function setHashKey(obj, h) { - if (h) { - obj.$$hashKey = h; - } - else { - delete obj.$$hashKey; - } -} - -/** - * @ngdoc function - * @name angular.extend - * @function - * - * @description - * Extends the destination object `dst` by copying all of the properties from the `src` object(s) - * to `dst`. You can specify multiple `src` objects. - * - * @param {Object} dst Destination object. - * @param {...Object} src Source object(s). - * @returns {Object} Reference to `dst`. - */ -function extend(dst) { - var h = dst.$$hashKey; - forEach(arguments, function(obj){ - if (obj !== dst) { - forEach(obj, function(value, key){ - dst[key] = value; - }); - } - }); - - setHashKey(dst,h); - return dst; -} - -function int(str) { - return parseInt(str, 10); -} - - -function inherit(parent, extra) { - return extend(new (extend(function() {}, {prototype:parent}))(), extra); -} - -var START_SPACE = /^\s*/; -var END_SPACE = /\s*$/; -function stripWhitespace(str) { - return isString(str) ? str.replace(START_SPACE, '').replace(END_SPACE, '') : str; -} - -/** - * @ngdoc function - * @name angular.noop - * @function - * - * @description - * A function that performs no operations. This function can be useful when writing code in the - * functional style. -
-     function foo(callback) {
-       var result = calculateResult();
-       (callback || angular.noop)(result);
-     }
-   
- */ -function noop() {} -noop.$inject = []; - - -/** - * @ngdoc function - * @name angular.identity - * @function - * - * @description - * A function that returns its first argument. This function is useful when writing code in the - * functional style. - * -
-     function transformer(transformationFn, value) {
-       return (transformationFn || identity)(value);
-     };
-   
- */ -function identity($) {return $;} -identity.$inject = []; - - -function valueFn(value) {return function() {return value;};} - -/** - * @ngdoc function - * @name angular.isUndefined - * @function - * - * @description - * Determines if a reference is undefined. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is undefined. - */ -function isUndefined(value){return typeof value == 'undefined';} - - -/** - * @ngdoc function - * @name angular.isDefined - * @function - * - * @description - * Determines if a reference is defined. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is defined. - */ -function isDefined(value){return typeof value != 'undefined';} - - -/** - * @ngdoc function - * @name angular.isObject - * @function - * - * @description - * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not - * considered to be objects. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is an `Object` but not `null`. - */ -function isObject(value){return value != null && typeof value == 'object';} - - -/** - * @ngdoc function - * @name angular.isString - * @function - * - * @description - * Determines if a reference is a `String`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `String`. - */ -function isString(value){return typeof value == 'string';} - - -/** - * @ngdoc function - * @name angular.isNumber - * @function - * - * @description - * Determines if a reference is a `Number`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Number`. - */ -function isNumber(value){return typeof value == 'number';} - - -/** - * @ngdoc function - * @name angular.isDate - * @function - * - * @description - * Determines if a value is a date. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Date`. - */ -function isDate(value){ - return toString.apply(value) == '[object Date]'; -} - - -/** - * @ngdoc function - * @name angular.isArray - * @function - * - * @description - * Determines if a reference is an `Array`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is an `Array`. - */ -function isArray(value) { - return toString.apply(value) == '[object Array]'; -} - - -/** - * @ngdoc function - * @name angular.isFunction - * @function - * - * @description - * Determines if a reference is a `Function`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Function`. - */ -function isFunction(value){return typeof value == 'function';} - - -/** - * Checks if `obj` is a window object. - * - * @private - * @param {*} obj Object to check - * @returns {boolean} True if `obj` is a window obj. - */ -function isWindow(obj) { - return obj && obj.document && obj.location && obj.alert && obj.setInterval; -} - - -function isScope(obj) { - return obj && obj.$evalAsync && obj.$watch; -} - - -function isFile(obj) { - return toString.apply(obj) === '[object File]'; -} - - -function isBoolean(value) { - return typeof value == 'boolean'; -} - - -function trim(value) { - return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; -} - -/** - * @ngdoc function - * @name angular.isElement - * @function - * - * @description - * Determines if a reference is a DOM element (or wrapped jQuery element). - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). - */ -function isElement(node) { - return node && - (node.nodeName // we are a direct element - || (node.bind && node.find)); // we have a bind and find method part of jQuery API -} - -/** - * @param str 'key1,key2,...' - * @returns {object} in the form of {key1:true, key2:true, ...} - */ -function makeMap(str){ - var obj = {}, items = str.split(","), i; - for ( i = 0; i < items.length; i++ ) - obj[ items[i] ] = true; - return obj; -} - - -if (msie < 9) { - nodeName_ = function(element) { - element = element.nodeName ? element : element[0]; - return (element.scopeName && element.scopeName != 'HTML') - ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName; - }; -} else { - nodeName_ = function(element) { - return element.nodeName ? element.nodeName : element[0].nodeName; - }; -} - - -function map(obj, iterator, context) { - var results = []; - forEach(obj, function(value, index, list) { - results.push(iterator.call(context, value, index, list)); - }); - return results; -} - - -/** - * @description - * Determines the number of elements in an array, the number of properties an object has, or - * the length of a string. - * - * Note: This function is used to augment the Object type in Angular expressions. See - * {@link angular.Object} for more information about Angular arrays. - * - * @param {Object|Array|string} obj Object, array, or string to inspect. - * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object - * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array. - */ -function size(obj, ownPropsOnly) { - var size = 0, key; - - if (isArray(obj) || isString(obj)) { - return obj.length; - } else if (isObject(obj)){ - for (key in obj) - if (!ownPropsOnly || obj.hasOwnProperty(key)) - size++; - } - - return size; -} - - -function includes(array, obj) { - return indexOf(array, obj) != -1; -} - -function indexOf(array, obj) { - if (array.indexOf) return array.indexOf(obj); - - for ( var i = 0; i < array.length; i++) { - if (obj === array[i]) return i; - } - return -1; -} - -function arrayRemove(array, value) { - var index = indexOf(array, value); - if (index >=0) - array.splice(index, 1); - return value; -} - -function isLeafNode (node) { - if (node) { - switch (node.nodeName) { - case "OPTION": - case "PRE": - case "TITLE": - return true; - } - } - return false; -} - -/** - * @ngdoc function - * @name angular.copy - * @function - * - * @description - * Creates a deep copy of `source`, which should be an object or an array. - * - * * If no destination is supplied, a copy of the object or array is created. - * * If a destination is provided, all of its elements (for array) or properties (for objects) - * are deleted and then all elements/properties from the source are copied to it. - * * If `source` is not an object or array, `source` is returned. - * - * Note: this function is used to augment the Object type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * - * @param {*} source The source that will be used to make a copy. - * Can be any type, including primitives, `null`, and `undefined`. - * @param {(Object|Array)=} destination Destination into which the source is copied. If - * provided, must be of the same type as `source`. - * @returns {*} The copy or updated `destination`, if `destination` was specified. - */ -function copy(source, destination){ - if (isWindow(source) || isScope(source)) throw Error("Can't copy Window or Scope"); - if (!destination) { - destination = source; - if (source) { - if (isArray(source)) { - destination = copy(source, []); - } else if (isDate(source)) { - destination = new Date(source.getTime()); - } else if (isObject(source)) { - destination = copy(source, {}); - } - } - } else { - if (source === destination) throw Error("Can't copy equivalent objects or arrays"); - if (isArray(source)) { - destination.length = 0; - for ( var i = 0; i < source.length; i++) { - destination.push(copy(source[i])); - } - } else { - var h = destination.$$hashKey; - forEach(destination, function(value, key){ - delete destination[key]; - }); - for ( var key in source) { - destination[key] = copy(source[key]); - } - setHashKey(destination,h); - } - } - return destination; -} - -/** - * Create a shallow copy of an object - */ -function shallowCopy(src, dst) { - dst = dst || {}; - - for(var key in src) { - if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') { - dst[key] = src[key]; - } - } - - return dst; -} - - -/** - * @ngdoc function - * @name angular.equals - * @function - * - * @description - * Determines if two objects or two values are equivalent. Supports value types, arrays and - * objects. - * - * Two objects or values are considered equivalent if at least one of the following is true: - * - * * Both objects or values pass `===` comparison. - * * Both objects or values are of the same type and all of their properties pass `===` comparison. - * * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal) - * - * During a property comparison, properties of `function` type and properties with names - * that begin with `$` are ignored. - * - * Scope and DOMWindow objects are being compared only by identify (`===`). - * - * @param {*} o1 Object or value to compare. - * @param {*} o2 Object or value to compare. - * @returns {boolean} True if arguments are equal. - */ -function equals(o1, o2) { - if (o1 === o2) return true; - if (o1 === null || o2 === null) return false; - if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN - var t1 = typeof o1, t2 = typeof o2, length, key, keySet; - if (t1 == t2) { - if (t1 == 'object') { - if (isArray(o1)) { - if ((length = o1.length) == o2.length) { - for(key=0; key 2 ? sliceArgs(arguments, 2) : []; - if (isFunction(fn) && !(fn instanceof RegExp)) { - return curryArgs.length - ? function() { - return arguments.length - ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0))) - : fn.apply(self, curryArgs); - } - : function() { - return arguments.length - ? fn.apply(self, arguments) - : fn.call(self); - }; - } else { - // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) - return fn; - } -} - - -function toJsonReplacer(key, value) { - var val = value; - - if (/^\$+/.test(key)) { - val = undefined; - } else if (isWindow(value)) { - val = '$WINDOW'; - } else if (value && document === value) { - val = '$DOCUMENT'; - } else if (isScope(value)) { - val = '$SCOPE'; - } - - return val; -} - - -/** - * @ngdoc function - * @name angular.toJson - * @function - * - * @description - * Serializes input into a JSON-formatted string. - * - * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. - * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace. - * @returns {string} Jsonified string representing `obj`. - */ -function toJson(obj, pretty) { - return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null); -} - - -/** - * @ngdoc function - * @name angular.fromJson - * @function - * - * @description - * Deserializes a JSON string. - * - * @param {string} json JSON string to deserialize. - * @returns {Object|Array|Date|string|number} Deserialized thingy. - */ -function fromJson(json) { - return isString(json) - ? JSON.parse(json) - : json; -} - - -function toBoolean(value) { - if (value && value.length !== 0) { - var v = lowercase("" + value); - value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]'); - } else { - value = false; - } - return value; -} - -/** - * @returns {string} Returns the string representation of the element. - */ -function startingTag(element) { - element = jqLite(element).clone(); - try { - // turns out IE does not let you set .html() on elements which - // are not allowed to have children. So we just ignore it. - element.html(''); - } catch(e) {} - // As Per DOM Standards - var TEXT_NODE = 3; - var elemHtml = jqLite('
').append(element).html(); - try { - return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) : - elemHtml. - match(/^(<[^>]+>)/)[1]. - replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); - } catch(e) { - return lowercase(elemHtml); - } - -} - - -///////////////////////////////////////////////// - -/** - * Parses an escaped url query string into key-value pairs. - * @returns Object.<(string|boolean)> - */ -function parseKeyValue(/**string*/keyValue) { - var obj = {}, key_value, key; - forEach((keyValue || "").split('&'), function(keyValue){ - if (keyValue) { - key_value = keyValue.split('='); - key = decodeURIComponent(key_value[0]); - obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true; - } - }); - return obj; -} - -function toKeyValue(obj) { - var parts = []; - forEach(obj, function(value, key) { - parts.push(encodeUriQuery(key, true) + (value === true ? '' : '=' + encodeUriQuery(value, true))); - }); - return parts.length ? parts.join('&') : ''; -} - - -/** - * We need our custom method because encodeURIComponent is too aggressive and doesn't follow - * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path - * segments: - * segment = *pchar - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * pct-encoded = "%" HEXDIG HEXDIG - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -function encodeUriSegment(val) { - return encodeUriQuery(val, true). - replace(/%26/gi, '&'). - replace(/%3D/gi, '='). - replace(/%2B/gi, '+'); -} - - -/** - * This method is intended for encoding *key* or *value* parts of query component. We need a custom - * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be - * encoded per http://tools.ietf.org/html/rfc3986: - * query = *( pchar / "/" / "?" ) - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * pct-encoded = "%" HEXDIG HEXDIG - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -function encodeUriQuery(val, pctEncodeSpaces) { - return encodeURIComponent(val). - replace(/%40/gi, '@'). - replace(/%3A/gi, ':'). - replace(/%24/g, '$'). - replace(/%2C/gi, ','). - replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); -} - - -/** - * @ngdoc directive - * @name ng.directive:ngApp - * - * @element ANY - * @param {angular.Module} ngApp an optional application - * {@link angular.module module} name to load. - * - * @description - * - * Use this directive to auto-bootstrap an application. Only - * one directive can be used per HTML document. The directive - * designates the root of the application and is typically placed - * at the root of the page. - * - * In the example below if the `ngApp` directive would not be placed - * on the `html` element then the document would not be compiled - * and the `{{ 1+2 }}` would not be resolved to `3`. - * - * `ngApp` is the easiest way to bootstrap an application. - * - - - I can add: 1 + 2 = {{ 1+2 }} - - - * - */ -function angularInit(element, bootstrap) { - var elements = [element], - appElement, - module, - names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'], - NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/; - - function append(element) { - element && elements.push(element); - } - - forEach(names, function(name) { - names[name] = true; - append(document.getElementById(name)); - name = name.replace(':', '\\:'); - if (element.querySelectorAll) { - forEach(element.querySelectorAll('.' + name), append); - forEach(element.querySelectorAll('.' + name + '\\:'), append); - forEach(element.querySelectorAll('[' + name + ']'), append); - } - }); - - forEach(elements, function(element) { - if (!appElement) { - var className = ' ' + element.className + ' '; - var match = NG_APP_CLASS_REGEXP.exec(className); - if (match) { - appElement = element; - module = (match[2] || '').replace(/\s+/g, ','); - } else { - forEach(element.attributes, function(attr) { - if (!appElement && names[attr.name]) { - appElement = element; - module = attr.value; - } - }); - } - } - }); - if (appElement) { - bootstrap(appElement, module ? [module] : []); - } -} - -/** - * @ngdoc function - * @name angular.bootstrap - * @description - * Use this function to manually start up angular application. - * - * See: {@link guide/bootstrap Bootstrap} - * - * @param {Element} element DOM element which is the root of angular application. - * @param {Array=} modules an array of module declarations. See: {@link angular.module modules} - * @returns {AUTO.$injector} Returns the newly created injector for this app. - */ -function bootstrap(element, modules) { - var resumeBootstrapInternal = function() { - element = jqLite(element); - modules = modules || []; - modules.unshift(['$provide', function($provide) { - $provide.value('$rootElement', element); - }]); - modules.unshift('ng'); - var injector = createInjector(modules); - injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animator', - function(scope, element, compile, injector, animator) { - scope.$apply(function() { - element.data('$injector', injector); - compile(element)(scope); - }); - animator.enabled(true); - }] - ); - return injector; - }; - - var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; - - if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { - return resumeBootstrapInternal(); - } - - window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); - angular.resumeBootstrap = function(extraModules) { - forEach(extraModules, function(module) { - modules.push(module); - }); - resumeBootstrapInternal(); - }; -} - -var SNAKE_CASE_REGEXP = /[A-Z]/g; -function snake_case(name, separator){ - separator = separator || '_'; - return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { - return (pos ? separator : '') + letter.toLowerCase(); - }); -} - -function bindJQuery() { - // bind to jQuery if present; - jQuery = window.jQuery; - // reset to jQuery or default to us. - if (jQuery) { - jqLite = jQuery; - extend(jQuery.fn, { - scope: JQLitePrototype.scope, - controller: JQLitePrototype.controller, - injector: JQLitePrototype.injector, - inheritedData: JQLitePrototype.inheritedData - }); - JQLitePatchJQueryRemove('remove', true); - JQLitePatchJQueryRemove('empty'); - JQLitePatchJQueryRemove('html'); - } else { - jqLite = JQLite; - } - angular.element = jqLite; -} - -/** - * throw error if the argument is falsy. - */ -function assertArg(arg, name, reason) { - if (!arg) { - throw new Error("Argument '" + (name || '?') + "' is " + (reason || "required")); - } - return arg; -} - -function assertArgFn(arg, name, acceptArrayAnnotation) { - if (acceptArrayAnnotation && isArray(arg)) { - arg = arg[arg.length - 1]; - } - - assertArg(isFunction(arg), name, 'not a function, got ' + - (arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg)); - return arg; -} - -/** - * @ngdoc interface - * @name angular.Module - * @description - * - * Interface for configuring angular {@link angular.module modules}. - */ - -function setupModuleLoader(window) { - - function ensure(obj, name, factory) { - return obj[name] || (obj[name] = factory()); - } - - return ensure(ensure(window, 'angular', Object), 'module', function() { - /** @type {Object.} */ - var modules = {}; - - /** - * @ngdoc function - * @name angular.module - * @description - * - * The `angular.module` is a global place for creating and registering Angular modules. All - * modules (angular core or 3rd party) that should be available to an application must be - * registered using this mechanism. - * - * - * # Module - * - * A module is a collocation of services, directives, filters, and configuration information. Module - * is used to configure the {@link AUTO.$injector $injector}. - * - *
-     * // Create a new module
-     * var myModule = angular.module('myModule', []);
-     *
-     * // register a new service
-     * myModule.value('appName', 'MyCoolApp');
-     *
-     * // configure existing services inside initialization blocks.
-     * myModule.config(function($locationProvider) {
-     *   // Configure existing providers
-     *   $locationProvider.hashPrefix('!');
-     * });
-     * 
- * - * Then you can create an injector and load your modules like this: - * - *
-     * var injector = angular.injector(['ng', 'MyModule'])
-     * 
- * - * However it's more likely that you'll just use - * {@link ng.directive:ngApp ngApp} or - * {@link angular.bootstrap} to simplify this process for you. - * - * @param {!string} name The name of the module to create or retrieve. - * @param {Array.=} requires If specified then new module is being created. If unspecified then the - * the module is being retrieved for further configuration. - * @param {Function} configFn Optional configuration function for the module. Same as - * {@link angular.Module#config Module#config()}. - * @returns {module} new module with the {@link angular.Module} api. - */ - return function module(name, requires, configFn) { - if (requires && modules.hasOwnProperty(name)) { - modules[name] = null; - } - return ensure(modules, name, function() { - if (!requires) { - throw Error('No module: ' + name); - } - - /** @type {!Array.>} */ - var invokeQueue = []; - - /** @type {!Array.} */ - var runBlocks = []; - - var config = invokeLater('$injector', 'invoke'); - - /** @type {angular.Module} */ - var moduleInstance = { - // Private state - _invokeQueue: invokeQueue, - _runBlocks: runBlocks, - - /** - * @ngdoc property - * @name angular.Module#requires - * @propertyOf angular.Module - * @returns {Array.} List of module names which must be loaded before this module. - * @description - * Holds the list of modules which the injector will load before the current module is loaded. - */ - requires: requires, - - /** - * @ngdoc property - * @name angular.Module#name - * @propertyOf angular.Module - * @returns {string} Name of the module. - * @description - */ - name: name, - - - /** - * @ngdoc method - * @name angular.Module#provider - * @methodOf angular.Module - * @param {string} name service name - * @param {Function} providerType Construction function for creating new instance of the service. - * @description - * See {@link AUTO.$provide#provider $provide.provider()}. - */ - provider: invokeLater('$provide', 'provider'), - - /** - * @ngdoc method - * @name angular.Module#factory - * @methodOf angular.Module - * @param {string} name service name - * @param {Function} providerFunction Function for creating new instance of the service. - * @description - * See {@link AUTO.$provide#factory $provide.factory()}. - */ - factory: invokeLater('$provide', 'factory'), - - /** - * @ngdoc method - * @name angular.Module#service - * @methodOf angular.Module - * @param {string} name service name - * @param {Function} constructor A constructor function that will be instantiated. - * @description - * See {@link AUTO.$provide#service $provide.service()}. - */ - service: invokeLater('$provide', 'service'), - - /** - * @ngdoc method - * @name angular.Module#value - * @methodOf angular.Module - * @param {string} name service name - * @param {*} object Service instance object. - * @description - * See {@link AUTO.$provide#value $provide.value()}. - */ - value: invokeLater('$provide', 'value'), - - /** - * @ngdoc method - * @name angular.Module#constant - * @methodOf angular.Module - * @param {string} name constant name - * @param {*} object Constant value. - * @description - * Because the constant are fixed, they get applied before other provide methods. - * See {@link AUTO.$provide#constant $provide.constant()}. - */ - constant: invokeLater('$provide', 'constant', 'unshift'), - - /** - * @ngdoc method - * @name angular.Module#animation - * @methodOf angular.Module - * @param {string} name animation name - * @param {Function} animationFactory Factory function for creating new instance of an animation. - * @description - * - * Defines an animation hook that can be later used with {@link ng.directive:ngAnimate ngAnimate} - * alongside {@link ng.directive:ngAnimate#Description common ng directives} as well as custom directives. - *
-           * module.animation('animation-name', function($inject1, $inject2) {
-           *   return {
-           *     //this gets called in preparation to setup an animation
-           *     setup : function(element) { ... },
-           *
-           *     //this gets called once the animation is run
-           *     start : function(element, done, memo) { ... }
-           *   }
-           * })
-           * 
- * - * See {@link ng.$animationProvider#register $animationProvider.register()} and - * {@link ng.directive:ngAnimate ngAnimate} for more information. - */ - animation: invokeLater('$animationProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#filter - * @methodOf angular.Module - * @param {string} name Filter name. - * @param {Function} filterFactory Factory function for creating new instance of filter. - * @description - * See {@link ng.$filterProvider#register $filterProvider.register()}. - */ - filter: invokeLater('$filterProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#controller - * @methodOf angular.Module - * @param {string} name Controller name. - * @param {Function} constructor Controller constructor function. - * @description - * See {@link ng.$controllerProvider#register $controllerProvider.register()}. - */ - controller: invokeLater('$controllerProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#directive - * @methodOf angular.Module - * @param {string} name directive name - * @param {Function} directiveFactory Factory function for creating new instance of - * directives. - * @description - * See {@link ng.$compileProvider#directive $compileProvider.directive()}. - */ - directive: invokeLater('$compileProvider', 'directive'), - - /** - * @ngdoc method - * @name angular.Module#config - * @methodOf angular.Module - * @param {Function} configFn Execute this function on module load. Useful for service - * configuration. - * @description - * Use this method to register work which needs to be performed on module loading. - */ - config: config, - - /** - * @ngdoc method - * @name angular.Module#run - * @methodOf angular.Module - * @param {Function} initializationFn Execute this function after injector creation. - * Useful for application initialization. - * @description - * Use this method to register work which should be performed when the injector is done - * loading all modules. - */ - run: function(block) { - runBlocks.push(block); - return this; - } - }; - - if (configFn) { - config(configFn); - } - - return moduleInstance; - - /** - * @param {string} provider - * @param {string} method - * @param {String=} insertMethod - * @returns {angular.Module} - */ - function invokeLater(provider, method, insertMethod) { - return function() { - invokeQueue[insertMethod || 'push']([provider, method, arguments]); - return moduleInstance; - } - } - }); - }; - }); - -} - -/** - * @ngdoc property - * @name angular.version - * @description - * An object that contains information about the current AngularJS version. This object has the - * following properties: - * - * - `full` – `{string}` – Full version string, such as "0.9.18". - * - `major` – `{number}` – Major version number, such as "0". - * - `minor` – `{number}` – Minor version number, such as "9". - * - `dot` – `{number}` – Dot version number, such as "18". - * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". - */ -var version = { - full: '1.1.5', // all of these placeholder strings will be replaced by grunt's - major: 1, // package task - minor: 1, - dot: 5, - codeName: 'triangle-squarification' -}; - - -function publishExternalAPI(angular){ - extend(angular, { - 'bootstrap': bootstrap, - 'copy': copy, - 'extend': extend, - 'equals': equals, - 'element': jqLite, - 'forEach': forEach, - 'injector': createInjector, - 'noop':noop, - 'bind':bind, - 'toJson': toJson, - 'fromJson': fromJson, - 'identity':identity, - 'isUndefined': isUndefined, - 'isDefined': isDefined, - 'isString': isString, - 'isFunction': isFunction, - 'isObject': isObject, - 'isNumber': isNumber, - 'isElement': isElement, - 'isArray': isArray, - 'version': version, - 'isDate': isDate, - 'lowercase': lowercase, - 'uppercase': uppercase, - 'callbacks': {counter: 0}, - 'noConflict': noConflict - }); - - angularModule = setupModuleLoader(window); - try { - angularModule('ngLocale'); - } catch (e) { - angularModule('ngLocale', []).provider('$locale', $LocaleProvider); - } - - angularModule('ng', ['ngLocale'], ['$provide', - function ngModule($provide) { - $provide.provider('$compile', $CompileProvider). - directive({ - a: htmlAnchorDirective, - input: inputDirective, - textarea: inputDirective, - form: formDirective, - script: scriptDirective, - select: selectDirective, - style: styleDirective, - option: optionDirective, - ngBind: ngBindDirective, - ngBindHtmlUnsafe: ngBindHtmlUnsafeDirective, - ngBindTemplate: ngBindTemplateDirective, - ngClass: ngClassDirective, - ngClassEven: ngClassEvenDirective, - ngClassOdd: ngClassOddDirective, - ngCsp: ngCspDirective, - ngCloak: ngCloakDirective, - ngController: ngControllerDirective, - ngForm: ngFormDirective, - ngHide: ngHideDirective, - ngIf: ngIfDirective, - ngInclude: ngIncludeDirective, - ngInit: ngInitDirective, - ngNonBindable: ngNonBindableDirective, - ngPluralize: ngPluralizeDirective, - ngRepeat: ngRepeatDirective, - ngShow: ngShowDirective, - ngSubmit: ngSubmitDirective, - ngStyle: ngStyleDirective, - ngSwitch: ngSwitchDirective, - ngSwitchWhen: ngSwitchWhenDirective, - ngSwitchDefault: ngSwitchDefaultDirective, - ngOptions: ngOptionsDirective, - ngView: ngViewDirective, - ngTransclude: ngTranscludeDirective, - ngModel: ngModelDirective, - ngList: ngListDirective, - ngChange: ngChangeDirective, - required: requiredDirective, - ngRequired: requiredDirective, - ngValue: ngValueDirective - }). - directive(ngAttributeAliasDirectives). - directive(ngEventDirectives); - $provide.provider({ - $anchorScroll: $AnchorScrollProvider, - $animation: $AnimationProvider, - $animator: $AnimatorProvider, - $browser: $BrowserProvider, - $cacheFactory: $CacheFactoryProvider, - $controller: $ControllerProvider, - $document: $DocumentProvider, - $exceptionHandler: $ExceptionHandlerProvider, - $filter: $FilterProvider, - $interpolate: $InterpolateProvider, - $http: $HttpProvider, - $httpBackend: $HttpBackendProvider, - $location: $LocationProvider, - $log: $LogProvider, - $parse: $ParseProvider, - $route: $RouteProvider, - $routeParams: $RouteParamsProvider, - $rootScope: $RootScopeProvider, - $q: $QProvider, - $sniffer: $SnifferProvider, - $templateCache: $TemplateCacheProvider, - $timeout: $TimeoutProvider, - $window: $WindowProvider - }); - } - ]); -} - -////////////////////////////////// -//JQLite -////////////////////////////////// - -/** - * @ngdoc function - * @name angular.element - * @function - * - * @description - * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. - * `angular.element` can be either an alias for [jQuery](http://api.jquery.com/jQuery/) function, if - * jQuery is available, or a function that wraps the element or string in Angular's jQuery lite - * implementation (commonly referred to as jqLite). - * - * Real jQuery always takes precedence over jqLite, provided it was loaded before `DOMContentLoaded` - * event fired. - * - * jqLite is a tiny, API-compatible subset of jQuery that allows - * Angular to manipulate the DOM. jqLite implements only the most commonly needed functionality - * within a very small footprint, so only a subset of the jQuery API - methods, arguments and - * invocation styles - are supported. - * - * Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never - * raw DOM references. - * - * ## Angular's jQuery lite provides the following methods: - * - * - [addClass()](http://api.jquery.com/addClass/) - * - [after()](http://api.jquery.com/after/) - * - [append()](http://api.jquery.com/append/) - * - [attr()](http://api.jquery.com/attr/) - * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces - * - [children()](http://api.jquery.com/children/) - Does not support selectors - * - [clone()](http://api.jquery.com/clone/) - * - [contents()](http://api.jquery.com/contents/) - * - [css()](http://api.jquery.com/css/) - * - [data()](http://api.jquery.com/data/) - * - [eq()](http://api.jquery.com/eq/) - * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name - * - [hasClass()](http://api.jquery.com/hasClass/) - * - [html()](http://api.jquery.com/html/) - * - [next()](http://api.jquery.com/next/) - Does not support selectors - * - [parent()](http://api.jquery.com/parent/) - Does not support selectors - * - [prepend()](http://api.jquery.com/prepend/) - * - [prop()](http://api.jquery.com/prop/) - * - [ready()](http://api.jquery.com/ready/) - * - [remove()](http://api.jquery.com/remove/) - * - [removeAttr()](http://api.jquery.com/removeAttr/) - * - [removeClass()](http://api.jquery.com/removeClass/) - * - [removeData()](http://api.jquery.com/removeData/) - * - [replaceWith()](http://api.jquery.com/replaceWith/) - * - [text()](http://api.jquery.com/text/) - * - [toggleClass()](http://api.jquery.com/toggleClass/) - * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. - * - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces - * - [val()](http://api.jquery.com/val/) - * - [wrap()](http://api.jquery.com/wrap/) - * - * ## In addition to the above, Angular provides additional methods to both jQuery and jQuery lite: - * - * - `controller(name)` - retrieves the controller of the current element or its parent. By default - * retrieves controller associated with the `ngController` directive. If `name` is provided as - * camelCase directive name, then the controller for this directive will be retrieved (e.g. - * `'ngModel'`). - * - `injector()` - retrieves the injector of the current element or its parent. - * - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current - * element or its parent. - * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top - * parent element is reached. - * - * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. - * @returns {Object} jQuery object. - */ - -var jqCache = JQLite.cache = {}, - jqName = JQLite.expando = 'ng-' + new Date().getTime(), - jqId = 1, - addEventListenerFn = (window.document.addEventListener - ? function(element, type, fn) {element.addEventListener(type, fn, false);} - : function(element, type, fn) {element.attachEvent('on' + type, fn);}), - removeEventListenerFn = (window.document.removeEventListener - ? function(element, type, fn) {element.removeEventListener(type, fn, false); } - : function(element, type, fn) {element.detachEvent('on' + type, fn); }); - -function jqNextId() { return ++jqId; } - - -var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; -var MOZ_HACK_REGEXP = /^moz([A-Z])/; - -/** - * Converts snake_case to camelCase. - * Also there is special case for Moz prefix starting with upper case letter. - * @param name Name to normalize - */ -function camelCase(name) { - return name. - replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { - return offset ? letter.toUpperCase() : letter; - }). - replace(MOZ_HACK_REGEXP, 'Moz$1'); -} - -///////////////////////////////////////////// -// jQuery mutation patch -// -// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a -// $destroy event on all DOM nodes being removed. -// -///////////////////////////////////////////// - -function JQLitePatchJQueryRemove(name, dispatchThis) { - var originalJqFn = jQuery.fn[name]; - originalJqFn = originalJqFn.$original || originalJqFn; - removePatch.$original = originalJqFn; - jQuery.fn[name] = removePatch; - - function removePatch() { - var list = [this], - fireEvent = dispatchThis, - set, setIndex, setLength, - element, childIndex, childLength, children, - fns, events; - - while(list.length) { - set = list.shift(); - for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) { - element = jqLite(set[setIndex]); - if (fireEvent) { - element.triggerHandler('$destroy'); - } else { - fireEvent = !fireEvent; - } - for(childIndex = 0, childLength = (children = element.children()).length; - childIndex < childLength; - childIndex++) { - list.push(jQuery(children[childIndex])); - } - } - } - return originalJqFn.apply(this, arguments); - } -} - -///////////////////////////////////////////// -function JQLite(element) { - if (element instanceof JQLite) { - return element; - } - if (!(this instanceof JQLite)) { - if (isString(element) && element.charAt(0) != '<') { - throw Error('selectors not implemented'); - } - return new JQLite(element); - } - - if (isString(element)) { - var div = document.createElement('div'); - // Read about the NoScope elements here: - // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx - div.innerHTML = '
 
' + element; // IE insanity to make NoScope elements work! - div.removeChild(div.firstChild); // remove the superfluous div - JQLiteAddNodes(this, div.childNodes); - this.remove(); // detach the elements from the temporary DOM div. - } else { - JQLiteAddNodes(this, element); - } -} - -function JQLiteClone(element) { - return element.cloneNode(true); -} - -function JQLiteDealoc(element){ - JQLiteRemoveData(element); - for ( var i = 0, children = element.childNodes || []; i < children.length; i++) { - JQLiteDealoc(children[i]); - } -} - -function JQLiteUnbind(element, type, fn) { - var events = JQLiteExpandoStore(element, 'events'), - handle = JQLiteExpandoStore(element, 'handle'); - - if (!handle) return; //no listeners registered - - if (isUndefined(type)) { - forEach(events, function(eventHandler, type) { - removeEventListenerFn(element, type, eventHandler); - delete events[type]; - }); - } else { - if (isUndefined(fn)) { - removeEventListenerFn(element, type, events[type]); - delete events[type]; - } else { - arrayRemove(events[type], fn); - } - } -} - -function JQLiteRemoveData(element) { - var expandoId = element[jqName], - expandoStore = jqCache[expandoId]; - - if (expandoStore) { - if (expandoStore.handle) { - expandoStore.events.$destroy && expandoStore.handle({}, '$destroy'); - JQLiteUnbind(element); - } - delete jqCache[expandoId]; - element[jqName] = undefined; // ie does not allow deletion of attributes on elements. - } -} - -function JQLiteExpandoStore(element, key, value) { - var expandoId = element[jqName], - expandoStore = jqCache[expandoId || -1]; - - if (isDefined(value)) { - if (!expandoStore) { - element[jqName] = expandoId = jqNextId(); - expandoStore = jqCache[expandoId] = {}; - } - expandoStore[key] = value; - } else { - return expandoStore && expandoStore[key]; - } -} - -function JQLiteData(element, key, value) { - var data = JQLiteExpandoStore(element, 'data'), - isSetter = isDefined(value), - keyDefined = !isSetter && isDefined(key), - isSimpleGetter = keyDefined && !isObject(key); - - if (!data && !isSimpleGetter) { - JQLiteExpandoStore(element, 'data', data = {}); - } - - if (isSetter) { - data[key] = value; - } else { - if (keyDefined) { - if (isSimpleGetter) { - // don't create data in this case. - return data && data[key]; - } else { - extend(data, key); - } - } else { - return data; - } - } -} - -function JQLiteHasClass(element, selector) { - return ((" " + element.className + " ").replace(/[\n\t]/g, " "). - indexOf( " " + selector + " " ) > -1); -} - -function JQLiteRemoveClass(element, cssClasses) { - if (cssClasses) { - forEach(cssClasses.split(' '), function(cssClass) { - element.className = trim( - (" " + element.className + " ") - .replace(/[\n\t]/g, " ") - .replace(" " + trim(cssClass) + " ", " ") - ); - }); - } -} - -function JQLiteAddClass(element, cssClasses) { - if (cssClasses) { - forEach(cssClasses.split(' '), function(cssClass) { - if (!JQLiteHasClass(element, cssClass)) { - element.className = trim(element.className + ' ' + trim(cssClass)); - } - }); - } -} - -function JQLiteAddNodes(root, elements) { - if (elements) { - elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements)) - ? elements - : [ elements ]; - for(var i=0; i < elements.length; i++) { - root.push(elements[i]); - } - } -} - -function JQLiteController(element, name) { - return JQLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); -} - -function JQLiteInheritedData(element, name, value) { - element = jqLite(element); - - // if element is the document object work with the html element instead - // this makes $(document).scope() possible - if(element[0].nodeType == 9) { - element = element.find('html'); - } - - while (element.length) { - if (value = element.data(name)) return value; - element = element.parent(); - } -} - -////////////////////////////////////////// -// Functions which are declared directly. -////////////////////////////////////////// -var JQLitePrototype = JQLite.prototype = { - ready: function(fn) { - var fired = false; - - function trigger() { - if (fired) return; - fired = true; - fn(); - } - - // check if document already is loaded - if (document.readyState === 'complete'){ - setTimeout(trigger); - } else { - this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9 - // we can not use jqLite since we are not done loading and jQuery could be loaded later. - JQLite(window).bind('load', trigger); // fallback to window.onload for others - } - }, - toString: function() { - var value = []; - forEach(this, function(e){ value.push('' + e);}); - return '[' + value.join(', ') + ']'; - }, - - eq: function(index) { - return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); - }, - - length: 0, - push: push, - sort: [].sort, - splice: [].splice -}; - -////////////////////////////////////////// -// Functions iterating getter/setters. -// these functions return self on setter and -// value on get. -////////////////////////////////////////// -var BOOLEAN_ATTR = {}; -forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { - BOOLEAN_ATTR[lowercase(value)] = value; -}); -var BOOLEAN_ELEMENTS = {}; -forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { - BOOLEAN_ELEMENTS[uppercase(value)] = true; -}); - -function getBooleanAttrName(element, name) { - // check dom last since we will most likely fail on name - var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; - - // booleanAttr is here twice to minimize DOM access - return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; -} - -forEach({ - data: JQLiteData, - inheritedData: JQLiteInheritedData, - - scope: function(element) { - return JQLiteInheritedData(element, '$scope'); - }, - - controller: JQLiteController , - - injector: function(element) { - return JQLiteInheritedData(element, '$injector'); - }, - - removeAttr: function(element,name) { - element.removeAttribute(name); - }, - - hasClass: JQLiteHasClass, - - css: function(element, name, value) { - name = camelCase(name); - - if (isDefined(value)) { - element.style[name] = value; - } else { - var val; - - if (msie <= 8) { - // this is some IE specific weirdness that jQuery 1.6.4 does not sure why - val = element.currentStyle && element.currentStyle[name]; - if (val === '') val = 'auto'; - } - - val = val || element.style[name]; - - if (msie <= 8) { - // jquery weirdness :-/ - val = (val === '') ? undefined : val; - } - - return val; - } - }, - - attr: function(element, name, value){ - var lowercasedName = lowercase(name); - if (BOOLEAN_ATTR[lowercasedName]) { - if (isDefined(value)) { - if (!!value) { - element[name] = true; - element.setAttribute(name, lowercasedName); - } else { - element[name] = false; - element.removeAttribute(lowercasedName); - } - } else { - return (element[name] || - (element.attributes.getNamedItem(name)|| noop).specified) - ? lowercasedName - : undefined; - } - } else if (isDefined(value)) { - element.setAttribute(name, value); - } else if (element.getAttribute) { - // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code - // some elements (e.g. Document) don't have get attribute, so return undefined - var ret = element.getAttribute(name, 2); - // normalize non-existing attributes to undefined (as jQuery) - return ret === null ? undefined : ret; - } - }, - - prop: function(element, name, value) { - if (isDefined(value)) { - element[name] = value; - } else { - return element[name]; - } - }, - - text: extend((msie < 9) - ? function(element, value) { - if (element.nodeType == 1 /** Element */) { - if (isUndefined(value)) - return element.innerText; - element.innerText = value; - } else { - if (isUndefined(value)) - return element.nodeValue; - element.nodeValue = value; - } - } - : function(element, value) { - if (isUndefined(value)) { - return element.textContent; - } - element.textContent = value; - }, {$dv:''}), - - val: function(element, value) { - if (isUndefined(value)) { - return element.value; - } - element.value = value; - }, - - html: function(element, value) { - if (isUndefined(value)) { - return element.innerHTML; - } - for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { - JQLiteDealoc(childNodes[i]); - } - element.innerHTML = value; - } -}, function(fn, name){ - /** - * Properties: writes return selection, reads return first value - */ - JQLite.prototype[name] = function(arg1, arg2) { - var i, key; - - // JQLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it - // in a way that survives minification. - if (((fn.length == 2 && (fn !== JQLiteHasClass && fn !== JQLiteController)) ? arg1 : arg2) === undefined) { - if (isObject(arg1)) { - - // we are a write, but the object properties are the key/values - for(i=0; i < this.length; i++) { - if (fn === JQLiteData) { - // data() takes the whole object in jQuery - fn(this[i], arg1); - } else { - for (key in arg1) { - fn(this[i], key, arg1[key]); - } - } - } - // return self for chaining - return this; - } else { - // we are a read, so read the first child. - if (this.length) - return fn(this[0], arg1, arg2); - } - } else { - // we are a write, so apply to all children - for(i=0; i < this.length; i++) { - fn(this[i], arg1, arg2); - } - // return self for chaining - return this; - } - return fn.$dv; - }; -}); - -function createEventHandler(element, events) { - var eventHandler = function (event, type) { - if (!event.preventDefault) { - event.preventDefault = function() { - event.returnValue = false; //ie - }; - } - - if (!event.stopPropagation) { - event.stopPropagation = function() { - event.cancelBubble = true; //ie - }; - } - - if (!event.target) { - event.target = event.srcElement || document; - } - - if (isUndefined(event.defaultPrevented)) { - var prevent = event.preventDefault; - event.preventDefault = function() { - event.defaultPrevented = true; - prevent.call(event); - }; - event.defaultPrevented = false; - } - - event.isDefaultPrevented = function() { - return event.defaultPrevented || event.returnValue == false; - }; - - forEach(events[type || event.type], function(fn) { - fn.call(element, event); - }); - - // Remove monkey-patched methods (IE), - // as they would cause memory leaks in IE8. - if (msie <= 8) { - // IE7/8 does not allow to delete property on native object - event.preventDefault = null; - event.stopPropagation = null; - event.isDefaultPrevented = null; - } else { - // It shouldn't affect normal browsers (native methods are defined on prototype). - delete event.preventDefault; - delete event.stopPropagation; - delete event.isDefaultPrevented; - } - }; - eventHandler.elem = element; - return eventHandler; -} - -////////////////////////////////////////// -// Functions iterating traversal. -// These functions chain results into a single -// selector. -////////////////////////////////////////// -forEach({ - removeData: JQLiteRemoveData, - - dealoc: JQLiteDealoc, - - bind: function bindFn(element, type, fn){ - var events = JQLiteExpandoStore(element, 'events'), - handle = JQLiteExpandoStore(element, 'handle'); - - if (!events) JQLiteExpandoStore(element, 'events', events = {}); - if (!handle) JQLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); - - forEach(type.split(' '), function(type){ - var eventFns = events[type]; - - if (!eventFns) { - if (type == 'mouseenter' || type == 'mouseleave') { - var contains = document.body.contains || document.body.compareDocumentPosition ? - 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; - }; - - events[type] = []; - - // Refer to jQuery's implementation of mouseenter & mouseleave - // Read about mouseenter and mouseleave: - // http://www.quirksmode.org/js/events_mouse.html#link8 - var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"} - bindFn(element, eventmap[type], function(event) { - var ret, target = this, related = event.relatedTarget; - // For mousenter/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 && !contains(target, related)) ){ - handle(event, type); - } - - }); - - } else { - addEventListenerFn(element, type, handle); - events[type] = []; - } - eventFns = events[type] - } - eventFns.push(fn); - }); - }, - - unbind: JQLiteUnbind, - - replaceWith: function(element, replaceNode) { - var index, parent = element.parentNode; - JQLiteDealoc(element); - forEach(new JQLite(replaceNode), function(node){ - if (index) { - parent.insertBefore(node, index.nextSibling); - } else { - parent.replaceChild(node, element); - } - index = node; - }); - }, - - children: function(element) { - var children = []; - forEach(element.childNodes, function(element){ - if (element.nodeType === 1) - children.push(element); - }); - return children; - }, - - contents: function(element) { - return element.childNodes || []; - }, - - append: function(element, node) { - forEach(new JQLite(node), function(child){ - if (element.nodeType === 1 || element.nodeType === 11) { - element.appendChild(child); - } - }); - }, - - prepend: function(element, node) { - if (element.nodeType === 1) { - var index = element.firstChild; - forEach(new JQLite(node), function(child){ - if (index) { - element.insertBefore(child, index); - } else { - element.appendChild(child); - index = child; - } - }); - } - }, - - wrap: function(element, wrapNode) { - wrapNode = jqLite(wrapNode)[0]; - var parent = element.parentNode; - if (parent) { - parent.replaceChild(wrapNode, element); - } - wrapNode.appendChild(element); - }, - - remove: function(element) { - JQLiteDealoc(element); - var parent = element.parentNode; - if (parent) parent.removeChild(element); - }, - - after: function(element, newElement) { - var index = element, parent = element.parentNode; - forEach(new JQLite(newElement), function(node){ - parent.insertBefore(node, index.nextSibling); - index = node; - }); - }, - - addClass: JQLiteAddClass, - removeClass: JQLiteRemoveClass, - - toggleClass: function(element, selector, condition) { - if (isUndefined(condition)) { - condition = !JQLiteHasClass(element, selector); - } - (condition ? JQLiteAddClass : JQLiteRemoveClass)(element, selector); - }, - - parent: function(element) { - var parent = element.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - - next: function(element) { - if (element.nextElementSibling) { - return element.nextElementSibling; - } - - // IE8 doesn't have nextElementSibling - var elm = element.nextSibling; - while (elm != null && elm.nodeType !== 1) { - elm = elm.nextSibling; - } - return elm; - }, - - find: function(element, selector) { - return element.getElementsByTagName(selector); - }, - - clone: JQLiteClone, - - triggerHandler: function(element, eventName) { - var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName]; - var event; - - forEach(eventFns, function(fn) { - fn.call(element, {preventDefault: noop}); - }); - } -}, function(fn, name){ - /** - * chaining functions - */ - JQLite.prototype[name] = function(arg1, arg2) { - var value; - for(var i=0; i < this.length; i++) { - if (value == undefined) { - value = fn(this[i], arg1, arg2); - if (value !== undefined) { - // any function which returns a value needs to be wrapped - value = jqLite(value); - } - } else { - JQLiteAddNodes(value, fn(this[i], arg1, arg2)); - } - } - return value == undefined ? this : value; - }; -}); - -/** - * Computes a hash of an 'obj'. - * Hash of a: - * string is string - * number is number as string - * object is either result of calling $$hashKey function on the object or uniquely generated id, - * that is also assigned to the $$hashKey property of the object. - * - * @param obj - * @returns {string} hash string such that the same input will have the same hash string. - * The resulting string key is in 'type:hashKey' format. - */ -function hashKey(obj) { - var objType = typeof obj, - key; - - if (objType == 'object' && obj !== null) { - if (typeof (key = obj.$$hashKey) == 'function') { - // must invoke on object to keep the right this - key = obj.$$hashKey(); - } else if (key === undefined) { - key = obj.$$hashKey = nextUid(); - } - } else { - key = obj; - } - - return objType + ':' + key; -} - -/** - * HashMap which can use objects as keys - */ -function HashMap(array){ - forEach(array, this.put, this); -} -HashMap.prototype = { - /** - * Store key value pair - * @param key key to store can be any type - * @param value value to store can be any type - */ - put: function(key, value) { - this[hashKey(key)] = value; - }, - - /** - * @param key - * @returns the value for the key - */ - get: function(key) { - return this[hashKey(key)]; - }, - - /** - * Remove the key/value pair - * @param key - */ - remove: function(key) { - var value = this[key = hashKey(key)]; - delete this[key]; - return value; - } -}; - -/** - * @ngdoc function - * @name angular.injector - * @function - * - * @description - * Creates an injector function that can be used for retrieving services as well as for - * dependency injection (see {@link guide/di dependency injection}). - * - - * @param {Array.} modules A list of module functions or their aliases. See - * {@link angular.module}. The `ng` module must be explicitly added. - * @returns {function()} Injector function. See {@link AUTO.$injector $injector}. - * - * @example - * Typical usage - *
- *   // create an injector
- *   var $injector = angular.injector(['ng']);
- *
- *   // use the injector to kick off your application
- *   // use the type inference to auto inject arguments, or use implicit injection
- *   $injector.invoke(function($rootScope, $compile, $document){
- *     $compile($document)($rootScope);
- *     $rootScope.$digest();
- *   });
- * 
- */ - - -/** - * @ngdoc overview - * @name AUTO - * @description - * - * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}. - */ - -var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; -var FN_ARG_SPLIT = /,/; -var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; -var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; -function annotate(fn) { - var $inject, - fnText, - argDecl, - last; - - if (typeof fn == 'function') { - if (!($inject = fn.$inject)) { - $inject = []; - fnText = fn.toString().replace(STRIP_COMMENTS, ''); - argDecl = fnText.match(FN_ARGS); - forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ - arg.replace(FN_ARG, function(all, underscore, name){ - $inject.push(name); - }); - }); - fn.$inject = $inject; - } - } else if (isArray(fn)) { - last = fn.length - 1; - assertArgFn(fn[last], 'fn'); - $inject = fn.slice(0, last); - } else { - assertArgFn(fn, 'fn', true); - } - return $inject; -} - -/////////////////////////////////////// - -/** - * @ngdoc object - * @name AUTO.$injector - * @function - * - * @description - * - * `$injector` is used to retrieve object instances as defined by - * {@link AUTO.$provide provider}, instantiate types, invoke methods, - * and load modules. - * - * The following always holds true: - * - *
- *   var $injector = angular.injector();
- *   expect($injector.get('$injector')).toBe($injector);
- *   expect($injector.invoke(function($injector){
- *     return $injector;
- *   }).toBe($injector);
- * 
- * - * # Injection Function Annotation - * - * JavaScript does not have annotations, and annotations are needed for dependency injection. The - * following are all valid ways of annotating function with injection arguments and are equivalent. - * - *
- *   // inferred (only works if code not minified/obfuscated)
- *   $injector.invoke(function(serviceA){});
- *
- *   // annotated
- *   function explicit(serviceA) {};
- *   explicit.$inject = ['serviceA'];
- *   $injector.invoke(explicit);
- *
- *   // inline
- *   $injector.invoke(['serviceA', function(serviceA){}]);
- * 
- * - * ## Inference - * - * In JavaScript calling `toString()` on a function returns the function definition. The definition can then be - * parsed and the function arguments can be extracted. *NOTE:* This does not work with minification, and obfuscation - * tools since these tools change the argument names. - * - * ## `$inject` Annotation - * By adding a `$inject` property onto a function the injection parameters can be specified. - * - * ## Inline - * As an array of injection names, where the last item in the array is the function to call. - */ - -/** - * @ngdoc method - * @name AUTO.$injector#get - * @methodOf AUTO.$injector - * - * @description - * Return an instance of the service. - * - * @param {string} name The name of the instance to retrieve. - * @return {*} The instance. - */ - -/** - * @ngdoc method - * @name AUTO.$injector#invoke - * @methodOf AUTO.$injector - * - * @description - * Invoke the method and supply the method arguments from the `$injector`. - * - * @param {!function} fn The function to invoke. The function arguments come form the function annotation. - * @param {Object=} self The `this` for the invoked method. - * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before - * the `$injector` is consulted. - * @returns {*} the value returned by the invoked `fn` function. - */ - -/** - * @ngdoc method - * @name AUTO.$injector#has - * @methodOf AUTO.$injector - * - * @description - * Allows the user to query if the particular service exist. - * - * @param {string} Name of the service to query. - * @returns {boolean} returns true if injector has given service. - */ - -/** - * @ngdoc method - * @name AUTO.$injector#instantiate - * @methodOf AUTO.$injector - * @description - * Create a new instance of JS type. The method takes a constructor function invokes the new operator and supplies - * all of the arguments to the constructor function as specified by the constructor annotation. - * - * @param {function} Type Annotated constructor function. - * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before - * the `$injector` is consulted. - * @returns {Object} new instance of `Type`. - */ - -/** - * @ngdoc method - * @name AUTO.$injector#annotate - * @methodOf AUTO.$injector - * - * @description - * Returns an array of service names which the function is requesting for injection. This API is used by the injector - * to determine which services need to be injected into the function when the function is invoked. There are three - * ways in which the function can be annotated with the needed dependencies. - * - * # Argument names - * - * The simplest form is to extract the dependencies from the arguments of the function. This is done by converting - * the function into a string using `toString()` method and extracting the argument names. - *
- *   // Given
- *   function MyController($scope, $route) {
- *     // ...
- *   }
- *
- *   // Then
- *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
- * 
- * - * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies - * are supported. - * - * # The `$inject` property - * - * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of - * services to be injected into the function. - *
- *   // Given
- *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
- *     // ...
- *   }
- *   // Define function dependencies
- *   MyController.$inject = ['$scope', '$route'];
- *
- *   // Then
- *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
- * 
- * - * # The array notation - * - * It is often desirable to inline Injected functions and that's when setting the `$inject` property is very - * inconvenient. In these situations using the array notation to specify the dependencies in a way that survives - * minification is a better choice: - * - *
- *   // We wish to write this (not minification / obfuscation safe)
- *   injector.invoke(function($compile, $rootScope) {
- *     // ...
- *   });
- *
- *   // We are forced to write break inlining
- *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
- *     // ...
- *   };
- *   tmpFn.$inject = ['$compile', '$rootScope'];
- *   injector.invoke(tmpFn);
- *
- *   // To better support inline function the inline annotation is supported
- *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
- *     // ...
- *   }]);
- *
- *   // Therefore
- *   expect(injector.annotate(
- *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
- *    ).toEqual(['$compile', '$rootScope']);
- * 
- * - * @param {function|Array.} fn Function for which dependent service names need to be retrieved as described - * above. - * - * @returns {Array.} The names of the services which the function requires. - */ - - - - -/** - * @ngdoc object - * @name AUTO.$provide - * - * @description - * - * Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance. - * The providers share the same name as the instance they create with `Provider` suffixed to them. - * - * A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of - * a service. The Provider can have additional methods which would allow for configuration of the provider. - * - *
- *   function GreetProvider() {
- *     var salutation = 'Hello';
- *
- *     this.salutation = function(text) {
- *       salutation = text;
- *     };
- *
- *     this.$get = function() {
- *       return function (name) {
- *         return salutation + ' ' + name + '!';
- *       };
- *     };
- *   }
- *
- *   describe('Greeter', function(){
- *
- *     beforeEach(module(function($provide) {
- *       $provide.provider('greet', GreetProvider);
- *     }));
- *
- *     it('should greet', inject(function(greet) {
- *       expect(greet('angular')).toEqual('Hello angular!');
- *     }));
- *
- *     it('should allow configuration of salutation', function() {
- *       module(function(greetProvider) {
- *         greetProvider.salutation('Ahoj');
- *       });
- *       inject(function(greet) {
- *         expect(greet('angular')).toEqual('Ahoj angular!');
- *       });
- *     });
- * 
- */ - -/** - * @ngdoc method - * @name AUTO.$provide#provider - * @methodOf AUTO.$provide - * @description - * - * Register a provider for a service. The providers can be retrieved and can have additional configuration methods. - * - * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provider'` key. - * @param {(Object|function())} provider If the provider is: - * - * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using - * {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created. - * - `Constructor`: a new instance of the provider will be created using - * {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`. - * - * @returns {Object} registered provider instance - */ - -/** - * @ngdoc method - * @name AUTO.$provide#factory - * @methodOf AUTO.$provide - * @description - * - * A short hand for configuring services if only `$get` method is required. - * - * @param {string} name The name of the instance. - * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand for - * `$provide.provider(name, {$get: $getFn})`. - * @returns {Object} registered provider instance - */ - - -/** - * @ngdoc method - * @name AUTO.$provide#service - * @methodOf AUTO.$provide - * @description - * - * A short hand for registering service of given class. - * - * @param {string} name The name of the instance. - * @param {Function} constructor A class (constructor function) that will be instantiated. - * @returns {Object} registered provider instance - */ - - -/** - * @ngdoc method - * @name AUTO.$provide#value - * @methodOf AUTO.$provide - * @description - * - * A short hand for configuring services if the `$get` method is a constant. - * - * @param {string} name The name of the instance. - * @param {*} value The value. - * @returns {Object} registered provider instance - */ - - -/** - * @ngdoc method - * @name AUTO.$provide#constant - * @methodOf AUTO.$provide - * @description - * - * A constant value, but unlike {@link AUTO.$provide#value value} it can be injected - * into configuration function (other modules) and it is not interceptable by - * {@link AUTO.$provide#decorator decorator}. - * - * @param {string} name The name of the constant. - * @param {*} value The constant value. - * @returns {Object} registered instance - */ - - -/** - * @ngdoc method - * @name AUTO.$provide#decorator - * @methodOf AUTO.$provide - * @description - * - * Decoration of service, allows the decorator to intercept the service instance creation. The - * returned instance may be the original instance, or a new instance which delegates to the - * original instance. - * - * @param {string} name The name of the service to decorate. - * @param {function()} decorator This function will be invoked when the service needs to be - * instantiated. The function is called using the {@link AUTO.$injector#invoke - * injector.invoke} method and is therefore fully injectable. Local injection arguments: - * - * * `$delegate` - The original service instance, which can be monkey patched, configured, - * decorated or delegated to. - */ - - -function createInjector(modulesToLoad) { - var INSTANTIATING = {}, - providerSuffix = 'Provider', - path = [], - loadedModules = new HashMap(), - providerCache = { - $provide: { - provider: supportObject(provider), - factory: supportObject(factory), - service: supportObject(service), - value: supportObject(value), - constant: supportObject(constant), - decorator: decorator - } - }, - providerInjector = (providerCache.$injector = - createInternalInjector(providerCache, function() { - throw Error("Unknown provider: " + path.join(' <- ')); - })), - instanceCache = {}, - instanceInjector = (instanceCache.$injector = - createInternalInjector(instanceCache, function(servicename) { - var provider = providerInjector.get(servicename + providerSuffix); - return instanceInjector.invoke(provider.$get, provider); - })); - - - forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); - - return instanceInjector; - - //////////////////////////////////// - // $provider - //////////////////////////////////// - - function supportObject(delegate) { - return function(key, value) { - if (isObject(key)) { - forEach(key, reverseParams(delegate)); - } else { - return delegate(key, value); - } - } - } - - function provider(name, provider_) { - if (isFunction(provider_) || isArray(provider_)) { - provider_ = providerInjector.instantiate(provider_); - } - if (!provider_.$get) { - throw Error('Provider ' + name + ' must define $get factory method.'); - } - return providerCache[name + providerSuffix] = provider_; - } - - function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } - - function service(name, constructor) { - return factory(name, ['$injector', function($injector) { - return $injector.instantiate(constructor); - }]); - } - - function value(name, value) { return factory(name, valueFn(value)); } - - function constant(name, value) { - providerCache[name] = value; - instanceCache[name] = value; - } - - function decorator(serviceName, decorFn) { - var origProvider = providerInjector.get(serviceName + providerSuffix), - orig$get = origProvider.$get; - - origProvider.$get = function() { - var origInstance = instanceInjector.invoke(orig$get, origProvider); - return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); - }; - } - - //////////////////////////////////// - // Module Loading - //////////////////////////////////// - function loadModules(modulesToLoad){ - var runBlocks = []; - forEach(modulesToLoad, function(module) { - if (loadedModules.get(module)) return; - loadedModules.put(module, true); - if (isString(module)) { - var moduleFn = angularModule(module); - runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); - - try { - for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { - var invokeArgs = invokeQueue[i], - provider = providerInjector.get(invokeArgs[0]); - - provider[invokeArgs[1]].apply(provider, invokeArgs[2]); - } - } catch (e) { - if (e.message) e.message += ' from ' + module; - throw e; - } - } else if (isFunction(module)) { - try { - runBlocks.push(providerInjector.invoke(module)); - } catch (e) { - if (e.message) e.message += ' from ' + module; - throw e; - } - } else if (isArray(module)) { - try { - runBlocks.push(providerInjector.invoke(module)); - } catch (e) { - if (e.message) e.message += ' from ' + String(module[module.length - 1]); - throw e; - } - } else { - assertArgFn(module, 'module'); - } - }); - return runBlocks; - } - - //////////////////////////////////// - // internal Injector - //////////////////////////////////// - - function createInternalInjector(cache, factory) { - - function getService(serviceName) { - if (typeof serviceName !== 'string') { - throw Error('Service name expected'); - } - if (cache.hasOwnProperty(serviceName)) { - if (cache[serviceName] === INSTANTIATING) { - throw Error('Circular dependency: ' + path.join(' <- ')); - } - return cache[serviceName]; - } else { - try { - path.unshift(serviceName); - cache[serviceName] = INSTANTIATING; - return cache[serviceName] = factory(serviceName); - } finally { - path.shift(); - } - } - } - - function invoke(fn, self, locals){ - var args = [], - $inject = annotate(fn), - length, i, - key; - - for(i = 0, length = $inject.length; i < length; i++) { - key = $inject[i]; - args.push( - locals && locals.hasOwnProperty(key) - ? locals[key] - : getService(key) - ); - } - if (!fn.$inject) { - // this means that we must be an array. - fn = fn[length]; - } - - - // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke - switch (self ? -1 : args.length) { - case 0: return fn(); - case 1: return fn(args[0]); - case 2: return fn(args[0], args[1]); - case 3: return fn(args[0], args[1], args[2]); - case 4: return fn(args[0], args[1], args[2], args[3]); - case 5: return fn(args[0], args[1], args[2], args[3], args[4]); - case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]); - case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); - case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); - case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); - case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); - default: return fn.apply(self, args); - } - } - - function instantiate(Type, locals) { - var Constructor = function() {}, - instance, returnedValue; - - // Check if Type is annotated and use just the given function at n-1 as parameter - // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); - Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; - instance = new Constructor(); - returnedValue = invoke(Type, instance, locals); - - return isObject(returnedValue) ? returnedValue : instance; - } - - return { - invoke: invoke, - instantiate: instantiate, - get: getService, - annotate: annotate, - has: function(name) { - return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); - } - }; - } -} - -/** - * @ngdoc function - * @name ng.$anchorScroll - * @requires $window - * @requires $location - * @requires $rootScope - * - * @description - * When called, it checks current value of `$location.hash()` and scroll to related element, - * according to rules specified in - * {@link http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document Html5 spec}. - * - * It also watches the `$location.hash()` and scroll whenever it changes to match any anchor. - * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. - */ -function $AnchorScrollProvider() { - - var autoScrollingEnabled = true; - - this.disableAutoScrolling = function() { - autoScrollingEnabled = false; - }; - - this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { - var document = $window.document; - - // helper function to get first anchor from a NodeList - // can't use filter.filter, as it accepts only instances of Array - // and IE can't convert NodeList to an array using [].slice - // TODO(vojta): use filter if we change it to accept lists as well - function getFirstAnchor(list) { - var result = null; - forEach(list, function(element) { - if (!result && lowercase(element.nodeName) === 'a') result = element; - }); - return result; - } - - function scroll() { - var hash = $location.hash(), elm; - - // empty hash, scroll to the top of the page - if (!hash) $window.scrollTo(0, 0); - - // element with given id - else if ((elm = document.getElementById(hash))) elm.scrollIntoView(); - - // first anchor with given name :-D - else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView(); - - // no element and hash == 'top', scroll to the top of the page - else if (hash === 'top') $window.scrollTo(0, 0); - } - - // does not scroll when user clicks on anchor link that is currently on - // (no url change, no $location.hash() change), browser native does scroll - if (autoScrollingEnabled) { - $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, - function autoScrollWatchAction() { - $rootScope.$evalAsync(scroll); - }); - } - - return scroll; - }]; -} - - -/** - * @ngdoc object - * @name ng.$animationProvider - * @description - * - * The $AnimationProvider provider allows developers to register and access custom JavaScript animations directly inside - * of a module. - * - */ -$AnimationProvider.$inject = ['$provide']; -function $AnimationProvider($provide) { - var suffix = 'Animation'; - - /** - * @ngdoc function - * @name ng.$animation#register - * @methodOf ng.$animationProvider - * - * @description - * Registers a new injectable animation factory function. The factory function produces the animation object which - * has these two properties: - * - * * `setup`: `function(Element):*` A function which receives the starting state of the element. The purpose - * of this function is to get the element ready for animation. Optionally the function returns an memento which - * is passed to the `start` function. - * * `start`: `function(Element, doneFunction, *)` The element to animate, the `doneFunction` to be called on - * element animation completion, and an optional memento from the `setup` function. - * - * @param {string} name The name of the animation. - * @param {function} factory The factory function that will be executed to return the animation object. - * - */ - this.register = function(name, factory) { - $provide.factory(camelCase(name) + suffix, factory); - }; - - this.$get = ['$injector', function($injector) { - /** - * @ngdoc function - * @name ng.$animation - * @function - * - * @description - * The $animation service is used to retrieve any defined animation functions. When executed, the $animation service - * will return a object that contains the setup and start functions that were defined for the animation. - * - * @param {String} name Name of the animation function to retrieve. Animation functions are registered and stored - * inside of the AngularJS DI so a call to $animate('custom') is the same as injecting `customAnimation` - * via dependency injection. - * @return {Object} the animation object which contains the `setup` and `start` functions that perform the animation. - */ - return function $animation(name) { - if (name) { - var animationName = camelCase(name) + suffix; - if ($injector.has(animationName)) { - return $injector.get(animationName); - } - } - }; - }]; -} - -// NOTE: this is a pseudo directive. - -/** - * @ngdoc directive - * @name ng.directive:ngAnimate - * - * @description - * The `ngAnimate` directive works as an attribute that is attached alongside pre-existing directives. - * It effects how the directive will perform DOM manipulation. This allows for complex animations to take place - * without burdening the directive which uses the animation with animation details. The built in directives - * `ngRepeat`, `ngInclude`, `ngSwitch`, `ngShow`, `ngHide` and `ngView` already accept `ngAnimate` directive. - * Custom directives can take advantage of animation through {@link ng.$animator $animator service}. - * - * Below is a more detailed breakdown of the supported callback events provided by pre-exisitng ng directives: - * - * | Directive | Supported Animations | - * |========================================================== |====================================================| - * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move | - * | {@link ng.directive:ngView#animations ngView} | enter and leave | - * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave | - * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave | - * | {@link ng.directive:ngIf#animations ngIf} | enter and leave | - * | {@link ng.directive:ngShow#animations ngShow & ngHide} | show and hide | - * - * You can find out more information about animations upon visiting each directive page. - * - * Below is an example of a directive that makes use of the ngAnimate attribute: - * - *
- * 
- * 
- *
- * 
- * 
- * 
- * 
- *
- * 
- * 
- * 
- * - * The `event1` and `event2` attributes refer to the animation events specific to the directive that has been assigned. - * - * Keep in mind that if an animation is running, no child element of such animation can also be animated. - * - *

CSS-defined Animations

- * By default, ngAnimate attaches two CSS classes per animation event to the DOM element to achieve the animation. - * It is up to you, the developer, to ensure that the animations take place using cross-browser CSS3 transitions as - * well as CSS animations. - * - * The following code below demonstrates how to perform animations using **CSS transitions** with ngAnimate: - * - *
- * 
- *
- * 
- *
- * - * The following code below demonstrates how to perform animations using **CSS animations** with ngAnimate: - * - *
- * 
- *
- * 
- *
- * - * ngAnimate will first examine any CSS animation code and then fallback to using CSS transitions. - * - * Upon DOM mutation, the event class is added first, then the browser is allowed to reflow the content and then, - * the active class is added to trigger the animation. The ngAnimate directive will automatically extract the duration - * of the animation to determine when the animation ends. Once the animation is over then both CSS classes will be - * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end - * immediately resulting in a DOM element that is at it's final state. This final state is when the DOM element - * has no CSS transition/animation classes surrounding it. - * - *

JavaScript-defined Animations

- * In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations to browsers that do not - * yet support them, then you can make use of JavaScript animations defined inside of your AngularJS module. - * - *
- * var ngModule = angular.module('YourApp', []);
- * ngModule.animation('animate-enter', function() {
- *   return {
- *     setup : function(element) {
- *       //prepare the element for animation
- *       element.css({ 'opacity': 0 });
- *       var memo = "..."; //this value is passed to the start function
- *       return memo;
- *     },
- *     start : function(element, done, memo) {
- *       //start the animation
- *       element.animate({
- *         'opacity' : 1
- *       }, function() {
- *         //call when the animation is complete
- *         done()
- *       });
- *     }
- *   }
- * });
- * 
- * - * As you can see, the JavaScript code follows a similar template to the CSS3 animations. Once defined, the animation - * can be used in the same way with the ngAnimate attribute. Keep in mind that, when using JavaScript-enabled - * animations, ngAnimate will also add in the same CSS classes that CSS-enabled animations do (even if you're not using - * CSS animations) to animated the element, but it will not attempt to find any CSS3 transition or animation duration/delay values. - * It will instead close off the animation once the provided done function is executed. So it's important that you - * make sure your animations remember to fire off the done function once the animations are complete. - * - * @param {expression} ngAnimate Used to configure the DOM manipulation animations. - * - */ - -var $AnimatorProvider = function() { - var NG_ANIMATE_CONTROLLER = '$ngAnimateController'; - var rootAnimateController = {running:true}; - - this.$get = ['$animation', '$window', '$sniffer', '$rootElement', '$rootScope', - function($animation, $window, $sniffer, $rootElement, $rootScope) { - $rootElement.data(NG_ANIMATE_CONTROLLER, rootAnimateController); - - /** - * @ngdoc function - * @name ng.$animator - * @function - * - * @description - * The $animator.create service provides the DOM manipulation API which is decorated with animations. - * - * @param {Scope} scope the scope for the ng-animate. - * @param {Attributes} attr the attributes object which contains the ngAnimate key / value pair. (The attributes are - * passed into the linking function of the directive using the `$animator`.) - * @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods. - */ - var AnimatorService = function(scope, attrs) { - var animator = {}; - - /** - * @ngdoc function - * @name ng.animator#enter - * @methodOf ng.$animator - * @function - * - * @description - * Injects the element object into the DOM (inside of the parent element) and then runs the enter animation. - * - * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation - * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation - * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation - */ - animator.enter = animateActionFactory('enter', insert, noop); - - /** - * @ngdoc function - * @name ng.animator#leave - * @methodOf ng.$animator - * @function - * - * @description - * Runs the leave animation operation and, upon completion, removes the element from the DOM. - * - * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation - * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the leave animation - */ - animator.leave = animateActionFactory('leave', noop, remove); - - /** - * @ngdoc function - * @name ng.animator#move - * @methodOf ng.$animator - * @function - * - * @description - * Fires the move DOM operation. Just before the animation starts, the animator will either append it into the parent container or - * add the element directly after the after element if present. Then the move animation will be run. - * - * @param {jQuery/jqLite element} element the element that will be the focus of the move animation - * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation - * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation - */ - animator.move = animateActionFactory('move', move, noop); - - /** - * @ngdoc function - * @name ng.animator#show - * @methodOf ng.$animator - * @function - * - * @description - * Reveals the element by setting the CSS property `display` to `block` and then starts the show animation directly after. - * - * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden - */ - animator.show = animateActionFactory('show', show, noop); - - /** - * @ngdoc function - * @name ng.animator#hide - * @methodOf ng.$animator - * - * @description - * Starts the hide animation first and sets the CSS `display` property to `none` upon completion. - * - * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden - */ - animator.hide = animateActionFactory('hide', noop, hide); - - /** - * @ngdoc function - * @name ng.animator#animate - * @methodOf ng.$animator - * - * @description - * Triggers a custom animation event to be executed on the given element - * - * @param {jQuery/jqLite element} element that will be animated - */ - animator.animate = function(event, element) { - animateActionFactory(event, noop, noop)(element); - } - return animator; - - function animateActionFactory(type, beforeFn, afterFn) { - return function(element, parent, after) { - var ngAnimateValue = scope.$eval(attrs.ngAnimate); - var className = ngAnimateValue - ? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type - : ''; - var animationPolyfill = $animation(className); - var polyfillSetup = animationPolyfill && animationPolyfill.setup; - var polyfillStart = animationPolyfill && animationPolyfill.start; - var polyfillCancel = animationPolyfill && animationPolyfill.cancel; - - if (!className) { - beforeFn(element, parent, after); - afterFn(element, parent, after); - } else { - var activeClassName = className + '-active'; - - if (!parent) { - parent = after ? after.parent() : element.parent(); - } - if ((!$sniffer.transitions && !polyfillSetup && !polyfillStart) || - (parent.inheritedData(NG_ANIMATE_CONTROLLER) || noop).running) { - beforeFn(element, parent, after); - afterFn(element, parent, after); - return; - } - - var animationData = element.data(NG_ANIMATE_CONTROLLER) || {}; - if(animationData.running) { - (polyfillCancel || noop)(element); - animationData.done(); - } - - element.data(NG_ANIMATE_CONTROLLER, {running:true, done:done}); - element.addClass(className); - beforeFn(element, parent, after); - if (element.length == 0) return done(); - - var memento = (polyfillSetup || noop)(element); - - // $window.setTimeout(beginAnimation, 0); this was causing the element not to animate - // keep at 1 for animation dom rerender - $window.setTimeout(beginAnimation, 1); - } - - function parseMaxTime(str) { - var total = 0, values = isString(str) ? str.split(/\s*,\s*/) : []; - forEach(values, function(value) { - total = Math.max(parseFloat(value) || 0, total); - }); - return total; - } - - function beginAnimation() { - element.addClass(activeClassName); - if (polyfillStart) { - polyfillStart(element, done, memento); - } else if (isFunction($window.getComputedStyle)) { - //one day all browsers will have these properties - var w3cAnimationProp = 'animation'; - var w3cTransitionProp = 'transition'; - - //but some still use vendor-prefixed styles - var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation'; - var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition'; - - var durationKey = 'Duration', - delayKey = 'Delay', - animationIterationCountKey = 'IterationCount', - duration = 0; - - //we want all the styles defined before and after - var ELEMENT_NODE = 1; - forEach(element, function(element) { - if (element.nodeType == ELEMENT_NODE) { - var w3cProp = w3cTransitionProp, - vendorProp = vendorTransitionProp, - iterations = 1, - elementStyles = $window.getComputedStyle(element) || {}; - - //use CSS Animations over CSS Transitions - if(parseFloat(elementStyles[w3cAnimationProp + durationKey]) > 0 || - parseFloat(elementStyles[vendorAnimationProp + durationKey]) > 0) { - w3cProp = w3cAnimationProp; - vendorProp = vendorAnimationProp; - iterations = Math.max(parseInt(elementStyles[w3cProp + animationIterationCountKey]) || 0, - parseInt(elementStyles[vendorProp + animationIterationCountKey]) || 0, - iterations); - } - - var parsedDelay = Math.max(parseMaxTime(elementStyles[w3cProp + delayKey]), - parseMaxTime(elementStyles[vendorProp + delayKey])); - - var parsedDuration = Math.max(parseMaxTime(elementStyles[w3cProp + durationKey]), - parseMaxTime(elementStyles[vendorProp + durationKey])); - - duration = Math.max(parsedDelay + (iterations * parsedDuration), duration); - } - }); - $window.setTimeout(done, duration * 1000); - } else { - done(); - } - } - - function done() { - if(!done.run) { - done.run = true; - afterFn(element, parent, after); - element.removeClass(className); - element.removeClass(activeClassName); - element.removeData(NG_ANIMATE_CONTROLLER); - } - } - }; - } - - function show(element) { - element.css('display', ''); - } - - function hide(element) { - element.css('display', 'none'); - } - - function insert(element, parent, after) { - if (after) { - after.after(element); - } else { - parent.append(element); - } - } - - function remove(element) { - element.remove(); - } - - function move(element, parent, after) { - // Do not remove element before insert. Removing will cause data associated with the - // element to be dropped. Insert will implicitly do the remove. - insert(element, parent, after); - } - }; - - /** - * @ngdoc function - * @name ng.animator#enabled - * @methodOf ng.$animator - * @function - * - * @param {Boolean=} If provided then set the animation on or off. - * @return {Boolean} Current animation state. - * - * @description - * Globally enables/disables animations. - * - */ - AnimatorService.enabled = function(value) { - if (arguments.length) { - rootAnimateController.running = !value; - } - return !rootAnimateController.running; - }; - - return AnimatorService; - }]; -}; - -/** - * ! This is a private undocumented service ! - * - * @name ng.$browser - * @requires $log - * @description - * This object has two goals: - * - * - hide all the global state in the browser caused by the window object - * - abstract away all the browser specific features and inconsistencies - * - * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` - * service, which can be used for convenient testing of the application without the interaction with - * the real browser apis. - */ -/** - * @param {object} window The global window object. - * @param {object} document jQuery wrapped document. - * @param {function()} XHR XMLHttpRequest constructor. - * @param {object} $log console.log or an object with the same interface. - * @param {object} $sniffer $sniffer service - */ -function Browser(window, document, $log, $sniffer) { - var self = this, - rawDocument = document[0], - location = window.location, - history = window.history, - setTimeout = window.setTimeout, - clearTimeout = window.clearTimeout, - pendingDeferIds = {}; - - self.isMock = false; - - var outstandingRequestCount = 0; - var outstandingRequestCallbacks = []; - - // TODO(vojta): remove this temporary api - self.$$completeOutstandingRequest = completeOutstandingRequest; - self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; - - /** - * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` - * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. - */ - function completeOutstandingRequest(fn) { - try { - fn.apply(null, sliceArgs(arguments, 1)); - } finally { - outstandingRequestCount--; - if (outstandingRequestCount === 0) { - while(outstandingRequestCallbacks.length) { - try { - outstandingRequestCallbacks.pop()(); - } catch (e) { - $log.error(e); - } - } - } - } - } - - /** - * @private - * Note: this method is used only by scenario runner - * TODO(vojta): prefix this method with $$ ? - * @param {function()} callback Function that will be called when no outstanding request - */ - self.notifyWhenNoOutstandingRequests = function(callback) { - // force browser to execute all pollFns - this is needed so that cookies and other pollers fire - // at some deterministic time in respect to the test runner's actions. Leaving things up to the - // regular poller would result in flaky tests. - forEach(pollFns, function(pollFn){ pollFn(); }); - - if (outstandingRequestCount === 0) { - callback(); - } else { - outstandingRequestCallbacks.push(callback); - } - }; - - ////////////////////////////////////////////////////////////// - // Poll Watcher API - ////////////////////////////////////////////////////////////// - var pollFns = [], - pollTimeout; - - /** - * @name ng.$browser#addPollFn - * @methodOf ng.$browser - * - * @param {function()} fn Poll function to add - * - * @description - * Adds a function to the list of functions that poller periodically executes, - * and starts polling if not started yet. - * - * @returns {function()} the added function - */ - self.addPollFn = function(fn) { - if (isUndefined(pollTimeout)) startPoller(100, setTimeout); - pollFns.push(fn); - return fn; - }; - - /** - * @param {number} interval How often should browser call poll functions (ms) - * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. - * - * @description - * Configures the poller to run in the specified intervals, using the specified - * setTimeout fn and kicks it off. - */ - function startPoller(interval, setTimeout) { - (function check() { - forEach(pollFns, function(pollFn){ pollFn(); }); - pollTimeout = setTimeout(check, interval); - })(); - } - - ////////////////////////////////////////////////////////////// - // URL API - ////////////////////////////////////////////////////////////// - - var lastBrowserUrl = location.href, - baseElement = document.find('base'); - - /** - * @name ng.$browser#url - * @methodOf ng.$browser - * - * @description - * GETTER: - * Without any argument, this method just returns current value of location.href. - * - * SETTER: - * With at least one argument, this method sets url to new value. - * If html5 history api supported, pushState/replaceState is used, otherwise - * location.href/location.replace is used. - * Returns its own instance to allow chaining - * - * NOTE: this api is intended for use only by the $location service. Please use the - * {@link ng.$location $location service} to change url. - * - * @param {string} url New url (when used as setter) - * @param {boolean=} replace Should new url replace current history record ? - */ - self.url = function(url, replace) { - // setter - if (url) { - if (lastBrowserUrl == url) return; - lastBrowserUrl = url; - if ($sniffer.history) { - if (replace) history.replaceState(null, '', url); - else { - history.pushState(null, '', url); - // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462 - baseElement.attr('href', baseElement.attr('href')); - } - } else { - if (replace) location.replace(url); - else location.href = url; - } - return self; - // getter - } else { - // the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 - return location.href.replace(/%27/g,"'"); - } - }; - - var urlChangeListeners = [], - urlChangeInit = false; - - function fireUrlChange() { - if (lastBrowserUrl == self.url()) return; - - lastBrowserUrl = self.url(); - forEach(urlChangeListeners, function(listener) { - listener(self.url()); - }); - } - - /** - * @name ng.$browser#onUrlChange - * @methodOf ng.$browser - * @TODO(vojta): refactor to use node's syntax for events - * - * @description - * Register callback function that will be called, when url changes. - * - * It's only called when the url is changed by outside of angular: - * - user types different url into address bar - * - user clicks on history (forward/back) button - * - user clicks on a link - * - * It's not called when url is changed by $browser.url() method - * - * The listener gets called with new url as parameter. - * - * NOTE: this api is intended for use only by the $location service. Please use the - * {@link ng.$location $location service} to monitor url changes in angular apps. - * - * @param {function(string)} listener Listener function to be called when url changes. - * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. - */ - self.onUrlChange = function(callback) { - if (!urlChangeInit) { - // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) - // don't fire popstate when user change the address bar and don't fire hashchange when url - // changed by push/replaceState - - // html5 history api - popstate event - if ($sniffer.history) jqLite(window).bind('popstate', fireUrlChange); - // hashchange event - if ($sniffer.hashchange) jqLite(window).bind('hashchange', fireUrlChange); - // polling - else self.addPollFn(fireUrlChange); - - urlChangeInit = true; - } - - urlChangeListeners.push(callback); - return callback; - }; - - ////////////////////////////////////////////////////////////// - // Misc API - ////////////////////////////////////////////////////////////// - - /** - * Returns current - * (always relative - without domain) - * - * @returns {string=} - */ - self.baseHref = function() { - var href = baseElement.attr('href'); - return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : ''; - }; - - ////////////////////////////////////////////////////////////// - // Cookies API - ////////////////////////////////////////////////////////////// - var lastCookies = {}; - var lastCookieString = ''; - var cookiePath = self.baseHref(); - - /** - * @name ng.$browser#cookies - * @methodOf ng.$browser - * - * @param {string=} name Cookie name - * @param {string=} value Cookie value - * - * @description - * The cookies method provides a 'private' low level access to browser cookies. - * It is not meant to be used directly, use the $cookie service instead. - * - * The return values vary depending on the arguments that the method was called with as follows: - *
    - *
  • cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it
  • - *
  • cookies(name, value) -> set name to value, if value is undefined delete the cookie
  • - *
  • cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)
  • - *
- * - * @returns {Object} Hash of all cookies (if called without any parameter) - */ - self.cookies = function(name, value) { - var cookieLength, cookieArray, cookie, i, index; - - if (name) { - if (value === undefined) { - rawDocument.cookie = escape(name) + "=;path=" + cookiePath + ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; - } else { - if (isString(value)) { - cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1; - - // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: - // - 300 cookies - // - 20 cookies per unique domain - // - 4096 bytes per cookie - if (cookieLength > 4096) { - $log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+ - cookieLength + " > 4096 bytes)!"); - } - } - } - } else { - if (rawDocument.cookie !== lastCookieString) { - lastCookieString = rawDocument.cookie; - cookieArray = lastCookieString.split("; "); - lastCookies = {}; - - for (i = 0; i < cookieArray.length; i++) { - cookie = cookieArray[i]; - index = cookie.indexOf('='); - if (index > 0) { //ignore nameless cookies - var name = unescape(cookie.substring(0, index)); - // the first value that is seen for a cookie is the most - // specific one. values for the same cookie name that - // follow are for less specific paths. - if (lastCookies[name] === undefined) { - lastCookies[name] = unescape(cookie.substring(index + 1)); - } - } - } - } - return lastCookies; - } - }; - - - /** - * @name ng.$browser#defer - * @methodOf ng.$browser - * @param {function()} fn A function, who's execution should be defered. - * @param {number=} [delay=0] of milliseconds to defer the function execution. - * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. - * - * @description - * Executes a fn asynchronously via `setTimeout(fn, delay)`. - * - * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using - * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed - * via `$browser.defer.flush()`. - * - */ - self.defer = function(fn, delay) { - var timeoutId; - outstandingRequestCount++; - timeoutId = setTimeout(function() { - delete pendingDeferIds[timeoutId]; - completeOutstandingRequest(fn); - }, delay || 0); - pendingDeferIds[timeoutId] = true; - return timeoutId; - }; - - - /** - * @name ng.$browser#defer.cancel - * @methodOf ng.$browser.defer - * - * @description - * Cancels a defered task identified with `deferId`. - * - * @param {*} deferId Token returned by the `$browser.defer` function. - * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully canceled. - */ - self.defer.cancel = function(deferId) { - if (pendingDeferIds[deferId]) { - delete pendingDeferIds[deferId]; - clearTimeout(deferId); - completeOutstandingRequest(noop); - return true; - } - return false; - }; - -} - -function $BrowserProvider(){ - this.$get = ['$window', '$log', '$sniffer', '$document', - function( $window, $log, $sniffer, $document){ - return new Browser($window, $document, $log, $sniffer); - }]; -} - -/** - * @ngdoc object - * @name ng.$cacheFactory - * - * @description - * Factory that constructs cache objects. - * - * - * @param {string} cacheId Name or id of the newly created cache. - * @param {object=} options Options object that specifies the cache behavior. Properties: - * - * - `{number=}` `capacity` — turns the cache into LRU cache. - * - * @returns {object} Newly created cache object with the following set of methods: - * - * - `{object}` `info()` — Returns id, size, and options of cache. - * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns it. - * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. - * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. - * - `{void}` `removeAll()` — Removes all cached values. - * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. - * - */ -function $CacheFactoryProvider() { - - this.$get = function() { - var caches = {}; - - function cacheFactory(cacheId, options) { - if (cacheId in caches) { - throw Error('cacheId ' + cacheId + ' taken'); - } - - var size = 0, - stats = extend({}, options, {id: cacheId}), - data = {}, - capacity = (options && options.capacity) || Number.MAX_VALUE, - lruHash = {}, - freshEnd = null, - staleEnd = null; - - return caches[cacheId] = { - - put: function(key, value) { - var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); - - refresh(lruEntry); - - if (isUndefined(value)) return; - if (!(key in data)) size++; - data[key] = value; - - if (size > capacity) { - this.remove(staleEnd.key); - } - - return value; - }, - - - get: function(key) { - var lruEntry = lruHash[key]; - - if (!lruEntry) return; - - refresh(lruEntry); - - return data[key]; - }, - - - remove: function(key) { - var lruEntry = lruHash[key]; - - if (!lruEntry) return; - - if (lruEntry == freshEnd) freshEnd = lruEntry.p; - if (lruEntry == staleEnd) staleEnd = lruEntry.n; - link(lruEntry.n,lruEntry.p); - - delete lruHash[key]; - delete data[key]; - size--; - }, - - - removeAll: function() { - data = {}; - size = 0; - lruHash = {}; - freshEnd = staleEnd = null; - }, - - - destroy: function() { - data = null; - stats = null; - lruHash = null; - delete caches[cacheId]; - }, - - - info: function() { - return extend({}, stats, {size: size}); - } - }; - - - /** - * makes the `entry` the freshEnd of the LRU linked list - */ - function refresh(entry) { - if (entry != freshEnd) { - if (!staleEnd) { - staleEnd = entry; - } else if (staleEnd == entry) { - staleEnd = entry.n; - } - - link(entry.n, entry.p); - link(entry, freshEnd); - freshEnd = entry; - freshEnd.n = null; - } - } - - - /** - * bidirectionally links two entries of the LRU linked list - */ - function link(nextEntry, prevEntry) { - if (nextEntry != prevEntry) { - if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify - if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify - } - } - } - - - cacheFactory.info = function() { - var info = {}; - forEach(caches, function(cache, cacheId) { - info[cacheId] = cache.info(); - }); - return info; - }; - - - cacheFactory.get = function(cacheId) { - return caches[cacheId]; - }; - - - return cacheFactory; - }; -} - -/** - * @ngdoc object - * @name ng.$templateCache - * - * @description - * Cache used for storing html templates. - * - * See {@link ng.$cacheFactory $cacheFactory}. - * - */ -function $TemplateCacheProvider() { - this.$get = ['$cacheFactory', function($cacheFactory) { - return $cacheFactory('templates'); - }]; -} - -/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! - * - * DOM-related variables: - * - * - "node" - DOM Node - * - "element" - DOM Element or Node - * - "$node" or "$element" - jqLite-wrapped node or element - * - * - * Compiler related stuff: - * - * - "linkFn" - linking fn of a single directive - * - "nodeLinkFn" - function that aggregates all linking fns for a particular node - * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node - * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) - */ - - -var NON_ASSIGNABLE_MODEL_EXPRESSION = 'Non-assignable model expression: '; - - -/** - * @ngdoc function - * @name ng.$compile - * @function - * - * @description - * Compiles a piece of HTML string or DOM into a template and produces a template function, which - * can then be used to link {@link ng.$rootScope.Scope scope} and the template together. - * - * The compilation is a process of walking the DOM tree and trying to match DOM elements to - * {@link ng.$compileProvider#directive directives}. For each match it - * executes corresponding template function and collects the - * instance functions into a single template function which is then returned. - * - * The template function can then be used once to produce the view or as it is the case with - * {@link ng.directive:ngRepeat repeater} many-times, in which - * case each call results in a view that is a DOM clone of the original template. - * - - - -
-
-
-
-
-
- - it('should auto compile', function() { - expect(element('div[compile]').text()).toBe('Hello Angular'); - input('html').enter('{{name}}!'); - expect(element('div[compile]').text()).toBe('Angular!'); - }); - -
- - * - * - * @param {string|DOMElement} element Element or HTML string to compile into a template function. - * @param {function(angular.Scope[, cloneAttachFn]} transclude function available to directives. - * @param {number} maxPriority only apply directives lower then given priority (Only effects the - * root element(s), not their children) - * @returns {function(scope[, cloneAttachFn])} a link function which is used to bind template - * (a DOM element/tree) to a scope. Where: - * - * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. - * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the - * `template` and call the `cloneAttachFn` function allowing the caller to attach the - * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is - * called as:
`cloneAttachFn(clonedElement, scope)` where: - * - * * `clonedElement` - is a clone of the original `element` passed into the compiler. - * * `scope` - is the current scope with which the linking function is working with. - * - * Calling the linking function returns the element of the template. It is either the original element - * passed in, or the clone of the element if the `cloneAttachFn` is provided. - * - * After linking the view is not updated until after a call to $digest which typically is done by - * Angular automatically. - * - * If you need access to the bound view, there are two ways to do it: - * - * - If you are not asking the linking function to clone the template, create the DOM element(s) - * before you send them to the compiler and keep this reference around. - *
- *     var element = $compile('

{{total}}

')(scope); - *
- * - * - if on the other hand, you need the element to be cloned, the view reference from the original - * example would not point to the clone, but rather to the original template that was cloned. In - * this case, you can access the clone via the cloneAttachFn: - *
- *     var templateHTML = angular.element('

{{total}}

'), - * scope = ....; - * - * var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) { - * //attach the clone to DOM document at the right place - * }); - * - * //now we have reference to the cloned DOM via `clone` - *
- * - * - * For information on how the compiler works, see the - * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. - */ - - -/** - * @ngdoc service - * @name ng.$compileProvider - * @function - * - * @description - */ -$CompileProvider.$inject = ['$provide']; -function $CompileProvider($provide) { - var hasDirectives = {}, - Suffix = 'Directive', - COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/, - CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/, - MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ', - urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/; - - - /** - * @ngdoc function - * @name ng.$compileProvider#directive - * @methodOf ng.$compileProvider - * @function - * - * @description - * Register a new directives with the compiler. - * - * @param {string} name Name of the directive in camel-case. (ie ngBind which will match as - * ng-bind). - * @param {function} directiveFactory An injectable directive factory function. See {@link guide/directive} for more - * info. - * @returns {ng.$compileProvider} Self for chaining. - */ - this.directive = function registerDirective(name, directiveFactory) { - if (isString(name)) { - assertArg(directiveFactory, 'directive'); - if (!hasDirectives.hasOwnProperty(name)) { - hasDirectives[name] = []; - $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', - function($injector, $exceptionHandler) { - var directives = []; - forEach(hasDirectives[name], function(directiveFactory) { - try { - var directive = $injector.invoke(directiveFactory); - if (isFunction(directive)) { - directive = { compile: valueFn(directive) }; - } else if (!directive.compile && directive.link) { - directive.compile = valueFn(directive.link); - } - directive.priority = directive.priority || 0; - directive.name = directive.name || name; - directive.require = directive.require || (directive.controller && directive.name); - directive.restrict = directive.restrict || 'A'; - directives.push(directive); - } catch (e) { - $exceptionHandler(e); - } - }); - return directives; - }]); - } - hasDirectives[name].push(directiveFactory); - } else { - forEach(name, reverseParams(registerDirective)); - } - return this; - }; - - - /** - * @ngdoc function - * @name ng.$compileProvider#urlSanitizationWhitelist - * @methodOf ng.$compileProvider - * @function - * - * @description - * Retrieves or overrides the default regular expression that is used for whitelisting of safe - * urls during a[href] sanitization. - * - * The sanitization is a security measure aimed at prevent XSS attacks via html links. - * - * Any url about to be assigned to a[href] via data-binding is first normalized and turned into an - * absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular - * expression. If a match is found the original url is written into the dom. Otherwise the - * absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM. - * - * @param {RegExp=} regexp New regexp to whitelist urls with. - * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for - * chaining otherwise. - */ - this.urlSanitizationWhitelist = function(regexp) { - if (isDefined(regexp)) { - urlSanitizationWhitelist = regexp; - return this; - } - return urlSanitizationWhitelist; - }; - - - this.$get = [ - '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse', - '$controller', '$rootScope', '$document', - function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, - $controller, $rootScope, $document) { - - var Attributes = function(element, attr) { - this.$$element = element; - this.$attr = attr || {}; - }; - - Attributes.prototype = { - $normalize: directiveNormalize, - - - /** - * Set a normalized attribute on the element in a way such that all directives - * can share the attribute. This function properly handles boolean attributes. - * @param {string} key Normalized key. (ie ngAttribute) - * @param {string|boolean} value The value to set. If `null` attribute will be deleted. - * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. - * Defaults to true. - * @param {string=} attrName Optional none normalized name. Defaults to key. - */ - $set: function(key, value, writeAttr, attrName) { - var booleanKey = getBooleanAttrName(this.$$element[0], key), - $$observers = this.$$observers, - normalizedVal; - - if (booleanKey) { - this.$$element.prop(key, value); - attrName = booleanKey; - } - - this[key] = value; - - // translate normalized key to actual key - if (attrName) { - this.$attr[key] = attrName; - } else { - attrName = this.$attr[key]; - if (!attrName) { - this.$attr[key] = attrName = snake_case(key, '-'); - } - } - - - // sanitize a[href] values - if (nodeName_(this.$$element[0]) === 'A' && key === 'href') { - urlSanitizationNode.setAttribute('href', value); - - // href property always returns normalized absolute url, so we can match against that - normalizedVal = urlSanitizationNode.href; - if (!normalizedVal.match(urlSanitizationWhitelist)) { - this[key] = value = 'unsafe:' + normalizedVal; - } - } - - - if (writeAttr !== false) { - if (value === null || value === undefined) { - this.$$element.removeAttr(attrName); - } else { - this.$$element.attr(attrName, value); - } - } - - // fire observers - $$observers && forEach($$observers[key], function(fn) { - try { - fn(value); - } catch (e) { - $exceptionHandler(e); - } - }); - }, - - - /** - * Observe an interpolated attribute. - * The observer will never be called, if given attribute is not interpolated. - * - * @param {string} key Normalized key. (ie ngAttribute) . - * @param {function(*)} fn Function that will be called whenever the attribute value changes. - * @returns {function(*)} the `fn` Function passed in. - */ - $observe: function(key, fn) { - var attrs = this, - $$observers = (attrs.$$observers || (attrs.$$observers = {})), - listeners = ($$observers[key] || ($$observers[key] = [])); - - listeners.push(fn); - $rootScope.$evalAsync(function() { - if (!listeners.$$inter) { - // no one registered attribute interpolation function, so lets call it manually - fn(attrs[key]); - } - }); - return fn; - } - }; - - var urlSanitizationNode = $document[0].createElement('a'), - startSymbol = $interpolate.startSymbol(), - endSymbol = $interpolate.endSymbol(), - denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') - ? identity - : function denormalizeTemplate(template) { - return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); - }, - NG_ATTR_BINDING = /^ngAttr[A-Z]/; - - - return compile; - - //================================ - - function compile($compileNodes, transcludeFn, maxPriority) { - if (!($compileNodes instanceof jqLite)) { - // jquery always rewraps, whereas we need to preserve the original selector so that we can modify it. - $compileNodes = jqLite($compileNodes); - } - // We can not compile top level text elements since text nodes can be merged and we will - // not be able to attach scope data to them, so we will wrap them in - forEach($compileNodes, function(node, index){ - if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) { - $compileNodes[index] = jqLite(node).wrap('').parent()[0]; - } - }); - var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority); - return function publicLinkFn(scope, cloneConnectFn){ - assertArg(scope, 'scope'); - // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart - // and sometimes changes the structure of the DOM. - var $linkNode = cloneConnectFn - ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!! - : $compileNodes; - - // Attach scope only to non-text nodes. - for(var i = 0, ii = $linkNode.length; i - addDirective(directives, - directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority); - - // iterate over the attributes - for (var attr, name, nName, ngAttrName, value, nAttrs = node.attributes, - j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { - attr = nAttrs[j]; - if (attr.specified) { - name = attr.name; - // support ngAttr attribute binding - ngAttrName = directiveNormalize(name); - if (NG_ATTR_BINDING.test(ngAttrName)) { - name = ngAttrName.substr(6).toLowerCase(); - } - nName = directiveNormalize(name.toLowerCase()); - attrsMap[nName] = name; - attrs[nName] = value = trim((msie && name == 'href') - ? decodeURIComponent(node.getAttribute(name, 2)) - : attr.value); - if (getBooleanAttrName(node, nName)) { - attrs[nName] = true; // presence means true - } - addAttrInterpolateDirective(node, directives, value, nName); - addDirective(directives, nName, 'A', maxPriority); - } - } - - // use class as directive - className = node.className; - if (isString(className) && className !== '') { - while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { - nName = directiveNormalize(match[2]); - if (addDirective(directives, nName, 'C', maxPriority)) { - attrs[nName] = trim(match[3]); - } - className = className.substr(match.index + match[0].length); - } - } - break; - case 3: /* Text Node */ - addTextInterpolateDirective(directives, node.nodeValue); - break; - case 8: /* Comment */ - try { - match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); - if (match) { - nName = directiveNormalize(match[1]); - if (addDirective(directives, nName, 'M', maxPriority)) { - attrs[nName] = trim(match[2]); - } - } - } catch (e) { - // turns out that under some circumstances IE9 throws errors when one attempts to read comment's node value. - // Just ignore it and continue. (Can't seem to reproduce in test case.) - } - break; - } - - directives.sort(byPriority); - return directives; - } - - - /** - * Once the directives have been collected, their compile functions are executed. This method - * is responsible for inlining directive templates as well as terminating the application - * of the directives if the terminal directive has been reached. - * - * @param {Array} directives Array of collected directives to execute their compile function. - * this needs to be pre-sorted by priority order. - * @param {Node} compileNode The raw DOM node to apply the compile functions to - * @param {Object} templateAttrs The shared attribute function - * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the - * scope argument is auto-generated to the new child of the transcluded parent scope. - * @param {JQLite} jqCollection If we are working on the root of the compile tree then this - * argument has the root jqLite array so that we can replace nodes on it. - * @returns linkFn - */ - function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) { - var terminalPriority = -Number.MAX_VALUE, - preLinkFns = [], - postLinkFns = [], - newScopeDirective = null, - newIsolateScopeDirective = null, - templateDirective = null, - $compileNode = templateAttrs.$$element = jqLite(compileNode), - directive, - directiveName, - $template, - transcludeDirective, - childTranscludeFn = transcludeFn, - controllerDirectives, - linkFn, - directiveValue; - - // executes all directives on the current element - for(var i = 0, ii = directives.length; i < ii; i++) { - directive = directives[i]; - $template = undefined; - - if (terminalPriority > directive.priority) { - break; // prevent further processing of directives - } - - if (directiveValue = directive.scope) { - assertNoDuplicate('isolated scope', newIsolateScopeDirective, directive, $compileNode); - if (isObject(directiveValue)) { - safeAddClass($compileNode, 'ng-isolate-scope'); - newIsolateScopeDirective = directive; - } - safeAddClass($compileNode, 'ng-scope'); - newScopeDirective = newScopeDirective || directive; - } - - directiveName = directive.name; - - if (directiveValue = directive.controller) { - controllerDirectives = controllerDirectives || {}; - assertNoDuplicate("'" + directiveName + "' controller", - controllerDirectives[directiveName], directive, $compileNode); - controllerDirectives[directiveName] = directive; - } - - if (directiveValue = directive.transclude) { - assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode); - transcludeDirective = directive; - terminalPriority = directive.priority; - if (directiveValue == 'element') { - $template = jqLite(compileNode); - $compileNode = templateAttrs.$$element = - jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' ')); - compileNode = $compileNode[0]; - replaceWith(jqCollection, jqLite($template[0]), compileNode); - childTranscludeFn = compile($template, transcludeFn, terminalPriority); - } else { - $template = jqLite(JQLiteClone(compileNode)).contents(); - $compileNode.html(''); // clear contents - childTranscludeFn = compile($template, transcludeFn); - } - } - - if (directive.template) { - assertNoDuplicate('template', templateDirective, directive, $compileNode); - templateDirective = directive; - - directiveValue = (isFunction(directive.template)) - ? directive.template($compileNode, templateAttrs) - : directive.template; - - directiveValue = denormalizeTemplate(directiveValue); - - if (directive.replace) { - $template = jqLite('
' + - trim(directiveValue) + - '
').contents(); - compileNode = $template[0]; - - if ($template.length != 1 || compileNode.nodeType !== 1) { - throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue); - } - - replaceWith(jqCollection, $compileNode, compileNode); - - var newTemplateAttrs = {$attr: {}}; - - // combine directives from the original node and from the template: - // - take the array of directives for this element - // - split it into two parts, those that were already applied and those that weren't - // - collect directives from the template, add them to the second group and sort them - // - append the second group with new directives to the first group - directives = directives.concat( - collectDirectives( - compileNode, - directives.splice(i + 1, directives.length - (i + 1)), - newTemplateAttrs - ) - ); - mergeTemplateAttributes(templateAttrs, newTemplateAttrs); - - ii = directives.length; - } else { - $compileNode.html(directiveValue); - } - } - - if (directive.templateUrl) { - assertNoDuplicate('template', templateDirective, directive, $compileNode); - templateDirective = directive; - nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), - nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace, - childTranscludeFn); - ii = directives.length; - } else if (directive.compile) { - try { - linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); - if (isFunction(linkFn)) { - addLinkFns(null, linkFn); - } else if (linkFn) { - addLinkFns(linkFn.pre, linkFn.post); - } - } catch (e) { - $exceptionHandler(e, startingTag($compileNode)); - } - } - - if (directive.terminal) { - nodeLinkFn.terminal = true; - terminalPriority = Math.max(terminalPriority, directive.priority); - } - - } - - nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope; - nodeLinkFn.transclude = transcludeDirective && childTranscludeFn; - - // might be normal or delayed nodeLinkFn depending on if templateUrl is present - return nodeLinkFn; - - //////////////////// - - function addLinkFns(pre, post) { - if (pre) { - pre.require = directive.require; - preLinkFns.push(pre); - } - if (post) { - post.require = directive.require; - postLinkFns.push(post); - } - } - - - function getControllers(require, $element) { - var value, retrievalMethod = 'data', optional = false; - if (isString(require)) { - while((value = require.charAt(0)) == '^' || value == '?') { - require = require.substr(1); - if (value == '^') { - retrievalMethod = 'inheritedData'; - } - optional = optional || value == '?'; - } - value = $element[retrievalMethod]('$' + require + 'Controller'); - if (!value && !optional) { - throw Error("No controller: " + require); - } - return value; - } else if (isArray(require)) { - value = []; - forEach(require, function(require) { - value.push(getControllers(require, $element)); - }); - } - return value; - } - - - function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { - var attrs, $element, i, ii, linkFn, controller; - - if (compileNode === linkNode) { - attrs = templateAttrs; - } else { - attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr)); - } - $element = attrs.$$element; - - if (newIsolateScopeDirective) { - var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/; - - var parentScope = scope.$parent || scope; - - forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) { - var match = definiton.match(LOCAL_REGEXP) || [], - attrName = match[3] || scopeName, - optional = (match[2] == '?'), - mode = match[1], // @, =, or & - lastValue, - parentGet, parentSet; - - scope.$$isolateBindings[scopeName] = mode + attrName; - - switch (mode) { - - case '@': { - attrs.$observe(attrName, function(value) { - scope[scopeName] = value; - }); - attrs.$$observers[attrName].$$scope = parentScope; - if( attrs[attrName] ) { - // If the attribute has been provided then we trigger an interpolation to ensure the value is there for use in the link fn - scope[scopeName] = $interpolate(attrs[attrName])(parentScope); - } - break; - } - - case '=': { - if (optional && !attrs[attrName]) { - return; - } - parentGet = $parse(attrs[attrName]); - parentSet = parentGet.assign || function() { - // reset the change, or we will throw this exception on every $digest - lastValue = scope[scopeName] = parentGet(parentScope); - throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + attrs[attrName] + - ' (directive: ' + newIsolateScopeDirective.name + ')'); - }; - lastValue = scope[scopeName] = parentGet(parentScope); - scope.$watch(function parentValueWatch() { - var parentValue = parentGet(parentScope); - - if (parentValue !== scope[scopeName]) { - // we are out of sync and need to copy - if (parentValue !== lastValue) { - // parent changed and it has precedence - lastValue = scope[scopeName] = parentValue; - } else { - // if the parent can be assigned then do so - parentSet(parentScope, parentValue = lastValue = scope[scopeName]); - } - } - return parentValue; - }); - break; - } - - case '&': { - parentGet = $parse(attrs[attrName]); - scope[scopeName] = function(locals) { - return parentGet(parentScope, locals); - }; - break; - } - - default: { - throw Error('Invalid isolate scope definition for directive ' + - newIsolateScopeDirective.name + ': ' + definiton); - } - } - }); - } - - if (controllerDirectives) { - forEach(controllerDirectives, function(directive) { - var locals = { - $scope: scope, - $element: $element, - $attrs: attrs, - $transclude: boundTranscludeFn - }; - - controller = directive.controller; - if (controller == '@') { - controller = attrs[directive.name]; - } - - $element.data( - '$' + directive.name + 'Controller', - $controller(controller, locals)); - }); - } - - // PRELINKING - for(i = 0, ii = preLinkFns.length; i < ii; i++) { - try { - linkFn = preLinkFns[i]; - linkFn(scope, $element, attrs, - linkFn.require && getControllers(linkFn.require, $element)); - } catch (e) { - $exceptionHandler(e, startingTag($element)); - } - } - - // RECURSION - childLinkFn && childLinkFn(scope, linkNode.childNodes, undefined, boundTranscludeFn); - - // POSTLINKING - for(i = 0, ii = postLinkFns.length; i < ii; i++) { - try { - linkFn = postLinkFns[i]; - linkFn(scope, $element, attrs, - linkFn.require && getControllers(linkFn.require, $element)); - } catch (e) { - $exceptionHandler(e, startingTag($element)); - } - } - } - } - - - /** - * looks up the directive and decorates it with exception handling and proper parameters. We - * call this the boundDirective. - * - * @param {string} name name of the directive to look up. - * @param {string} location The directive must be found in specific format. - * String containing any of theses characters: - * - * * `E`: element name - * * `A': attribute - * * `C`: class - * * `M`: comment - * @returns true if directive was added. - */ - function addDirective(tDirectives, name, location, maxPriority) { - var match = false; - if (hasDirectives.hasOwnProperty(name)) { - for(var directive, directives = $injector.get(name + Suffix), - i = 0, ii = directives.length; i directive.priority) && - directive.restrict.indexOf(location) != -1) { - tDirectives.push(directive); - match = true; - } - } catch(e) { $exceptionHandler(e); } - } - } - return match; - } - - - /** - * When the element is replaced with HTML template then the new attributes - * on the template need to be merged with the existing attributes in the DOM. - * The desired effect is to have both of the attributes present. - * - * @param {object} dst destination attributes (original DOM) - * @param {object} src source attributes (from the directive template) - */ - function mergeTemplateAttributes(dst, src) { - var srcAttr = src.$attr, - dstAttr = dst.$attr, - $element = dst.$$element; - - // reapply the old attributes to the new element - forEach(dst, function(value, key) { - if (key.charAt(0) != '$') { - if (src[key]) { - value += (key === 'style' ? ';' : ' ') + src[key]; - } - dst.$set(key, value, true, srcAttr[key]); - } - }); - - // copy the new attributes on the old attrs object - forEach(src, function(value, key) { - if (key == 'class') { - safeAddClass($element, value); - dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; - } else if (key == 'style') { - $element.attr('style', $element.attr('style') + ';' + value); - } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { - dst[key] = value; - dstAttr[key] = srcAttr[key]; - } - }); - } - - - function compileTemplateUrl(directives, beforeTemplateNodeLinkFn, $compileNode, tAttrs, - $rootElement, replace, childTranscludeFn) { - var linkQueue = [], - afterTemplateNodeLinkFn, - afterTemplateChildLinkFn, - beforeTemplateCompileNode = $compileNode[0], - origAsyncDirective = directives.shift(), - // The fact that we have to copy and patch the directive seems wrong! - derivedSyncDirective = extend({}, origAsyncDirective, { - controller: null, templateUrl: null, transclude: null, scope: null - }), - templateUrl = (isFunction(origAsyncDirective.templateUrl)) - ? origAsyncDirective.templateUrl($compileNode, tAttrs) - : origAsyncDirective.templateUrl; - - $compileNode.html(''); - - $http.get(templateUrl, {cache: $templateCache}). - success(function(content) { - var compileNode, tempTemplateAttrs, $template; - - content = denormalizeTemplate(content); - - if (replace) { - $template = jqLite('
' + trim(content) + '
').contents(); - compileNode = $template[0]; - - if ($template.length != 1 || compileNode.nodeType !== 1) { - throw new Error(MULTI_ROOT_TEMPLATE_ERROR + content); - } - - tempTemplateAttrs = {$attr: {}}; - replaceWith($rootElement, $compileNode, compileNode); - collectDirectives(compileNode, directives, tempTemplateAttrs); - mergeTemplateAttributes(tAttrs, tempTemplateAttrs); - } else { - compileNode = beforeTemplateCompileNode; - $compileNode.html(content); - } - - directives.unshift(derivedSyncDirective); - afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn); - afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); - - - while(linkQueue.length) { - var scope = linkQueue.shift(), - beforeTemplateLinkNode = linkQueue.shift(), - linkRootElement = linkQueue.shift(), - controller = linkQueue.shift(), - linkNode = compileNode; - - if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { - // it was cloned therefore we have to clone as well. - linkNode = JQLiteClone(compileNode); - replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); - } - - afterTemplateNodeLinkFn(function() { - beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, controller); - }, scope, linkNode, $rootElement, controller); - } - linkQueue = null; - }). - error(function(response, code, headers, config) { - throw Error('Failed to load template: ' + config.url); - }); - - return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) { - if (linkQueue) { - linkQueue.push(scope); - linkQueue.push(node); - linkQueue.push(rootElement); - linkQueue.push(controller); - } else { - afterTemplateNodeLinkFn(function() { - beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, controller); - }, scope, node, rootElement, controller); - } - }; - } - - - /** - * Sorting function for bound directives. - */ - function byPriority(a, b) { - return b.priority - a.priority; - } - - - function assertNoDuplicate(what, previousDirective, directive, element) { - if (previousDirective) { - throw Error('Multiple directives [' + previousDirective.name + ', ' + - directive.name + '] asking for ' + what + ' on: ' + startingTag(element)); - } - } - - - function addTextInterpolateDirective(directives, text) { - var interpolateFn = $interpolate(text, true); - if (interpolateFn) { - directives.push({ - priority: 0, - compile: valueFn(function textInterpolateLinkFn(scope, node) { - var parent = node.parent(), - bindings = parent.data('$binding') || []; - bindings.push(interpolateFn); - safeAddClass(parent.data('$binding', bindings), 'ng-binding'); - scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { - node[0].nodeValue = value; - }); - }) - }); - } - } - - - function addAttrInterpolateDirective(node, directives, value, name) { - var interpolateFn = $interpolate(value, true); - - // no interpolation found -> ignore - if (!interpolateFn) return; - - - directives.push({ - priority: 100, - compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) { - var $$observers = (attr.$$observers || (attr.$$observers = {})); - - // we need to interpolate again, in case the attribute value has been updated - // (e.g. by another directive's compile function) - interpolateFn = $interpolate(attr[name], true); - - // if attribute was updated so that there is no interpolation going on we don't want to - // register any observers - if (!interpolateFn) return; - - attr[name] = interpolateFn(scope); - ($$observers[name] || ($$observers[name] = [])).$$inter = true; - (attr.$$observers && attr.$$observers[name].$$scope || scope). - $watch(interpolateFn, function interpolateFnWatchAction(value) { - attr.$set(name, value); - }); - }) - }); - } - - - /** - * This is a special jqLite.replaceWith, which can replace items which - * have no parents, provided that the containing jqLite collection is provided. - * - * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes - * in the root of the tree. - * @param {JqLite} $element The jqLite element which we are going to replace. We keep the shell, - * but replace its DOM node reference. - * @param {Node} newNode The new DOM node. - */ - function replaceWith($rootElement, $element, newNode) { - var oldNode = $element[0], - parent = oldNode.parentNode, - i, ii; - - if ($rootElement) { - for(i = 0, ii = $rootElement.length; i < ii; i++) { - if ($rootElement[i] == oldNode) { - $rootElement[i] = newNode; - break; - } - } - } - - if (parent) { - parent.replaceChild(newNode, oldNode); - } - - newNode[jqLite.expando] = oldNode[jqLite.expando]; - $element[0] = newNode; - } - }]; -} - -var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; -/** - * Converts all accepted directives format into proper directive name. - * All of these will become 'myDirective': - * my:DiRective - * my-directive - * x-my-directive - * data-my:directive - * - * Also there is special case for Moz prefix starting with upper case letter. - * @param name Name to normalize - */ -function directiveNormalize(name) { - return camelCase(name.replace(PREFIX_REGEXP, '')); -} - -/** - * @ngdoc object - * @name ng.$compile.directive.Attributes - * @description - * - * A shared object between directive compile / linking functions which contains normalized DOM element - * attributes. The the values reflect current binding state `{{ }}`. The normalization is needed - * since all of these are treated as equivalent in Angular: - * - * - */ - -/** - * @ngdoc property - * @name ng.$compile.directive.Attributes#$attr - * @propertyOf ng.$compile.directive.Attributes - * @returns {object} A map of DOM element attribute names to the normalized name. This is - * needed to do reverse lookup from normalized name back to actual name. - */ - - -/** - * @ngdoc function - * @name ng.$compile.directive.Attributes#$set - * @methodOf ng.$compile.directive.Attributes - * @function - * - * @description - * Set DOM element attribute value. - * - * - * @param {string} name Normalized element attribute name of the property to modify. The name is - * revers translated using the {@link ng.$compile.directive.Attributes#$attr $attr} - * property to the original name. - * @param {string} value Value to set the attribute to. The value can be an interpolated string. - */ - - - -/** - * Closure compiler type information - */ - -function nodesetLinkingFn( - /* angular.Scope */ scope, - /* NodeList */ nodeList, - /* Element */ rootElement, - /* function(Function) */ boundTranscludeFn -){} - -function directiveLinkingFn( - /* nodesetLinkingFn */ nodesetLinkingFn, - /* angular.Scope */ scope, - /* Node */ node, - /* Element */ rootElement, - /* function(Function) */ boundTranscludeFn -){} - -/** - * @ngdoc object - * @name ng.$controllerProvider - * @description - * The {@link ng.$controller $controller service} is used by Angular to create new - * controllers. - * - * This provider allows controller registration via the - * {@link ng.$controllerProvider#register register} method. - */ -function $ControllerProvider() { - var controllers = {}, - CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/; - - - /** - * @ngdoc function - * @name ng.$controllerProvider#register - * @methodOf ng.$controllerProvider - * @param {string} name Controller name - * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI - * annotations in the array notation). - */ - this.register = function(name, constructor) { - if (isObject(name)) { - extend(controllers, name) - } else { - controllers[name] = constructor; - } - }; - - - this.$get = ['$injector', '$window', function($injector, $window) { - - /** - * @ngdoc function - * @name ng.$controller - * @requires $injector - * - * @param {Function|string} constructor If called with a function then it's considered to be the - * controller constructor function. Otherwise it's considered to be a string which is used - * to retrieve the controller constructor using the following steps: - * - * * check if a controller with given name is registered via `$controllerProvider` - * * check if evaluating the string on the current scope returns a constructor - * * check `window[constructor]` on the global `window` object - * - * @param {Object} locals Injection locals for Controller. - * @return {Object} Instance of given controller. - * - * @description - * `$controller` service is responsible for instantiating controllers. - * - * It's just a simple call to {@link AUTO.$injector $injector}, but extracted into - * a service, so that one can override this service with {@link https://gist.github.com/1649788 - * BC version}. - */ - return function(expression, locals) { - var instance, match, constructor, identifier; - - if(isString(expression)) { - match = expression.match(CNTRL_REG), - constructor = match[1], - identifier = match[3]; - expression = controllers.hasOwnProperty(constructor) - ? controllers[constructor] - : getter(locals.$scope, constructor, true) || getter($window, constructor, true); - - assertArgFn(expression, constructor, true); - } - - instance = $injector.instantiate(expression, locals); - - if (identifier) { - if (typeof locals.$scope !== 'object') { - throw new Error('Can not export controller as "' + identifier + '". ' + - 'No scope object provided!'); - } - - locals.$scope[identifier] = instance; - } - - return instance; - }; - }]; -} - -/** - * @ngdoc object - * @name ng.$document - * @requires $window - * - * @description - * A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document` - * element. - */ -function $DocumentProvider(){ - this.$get = ['$window', function(window){ - return jqLite(window.document); - }]; -} - -/** - * @ngdoc function - * @name ng.$exceptionHandler - * @requires $log - * - * @description - * Any uncaught exception in angular expressions is delegated to this service. - * The default implementation simply delegates to `$log.error` which logs it into - * the browser console. - * - * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by - * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. - * - * @param {Error} exception Exception associated with the error. - * @param {string=} cause optional information about the context in which - * the error was thrown. - * - */ -function $ExceptionHandlerProvider() { - this.$get = ['$log', function($log) { - return function(exception, cause) { - $log.error.apply($log, arguments); - }; - }]; -} - -/** - * @ngdoc object - * @name ng.$interpolateProvider - * @function - * - * @description - * - * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. - */ -function $InterpolateProvider() { - var startSymbol = '{{'; - var endSymbol = '}}'; - - /** - * @ngdoc method - * @name ng.$interpolateProvider#startSymbol - * @methodOf ng.$interpolateProvider - * @description - * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. - * - * @param {string=} value new value to set the starting symbol to. - * @returns {string|self} Returns the symbol when used as getter and self if used as setter. - */ - this.startSymbol = function(value){ - if (value) { - startSymbol = value; - return this; - } else { - return startSymbol; - } - }; - - /** - * @ngdoc method - * @name ng.$interpolateProvider#endSymbol - * @methodOf ng.$interpolateProvider - * @description - * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. - * - * @param {string=} value new value to set the ending symbol to. - * @returns {string|self} Returns the symbol when used as getter and self if used as setter. - */ - this.endSymbol = function(value){ - if (value) { - endSymbol = value; - return this; - } else { - return endSymbol; - } - }; - - - this.$get = ['$parse', '$exceptionHandler', function($parse, $exceptionHandler) { - var startSymbolLength = startSymbol.length, - endSymbolLength = endSymbol.length; - - /** - * @ngdoc function - * @name ng.$interpolate - * @function - * - * @requires $parse - * - * @description - * - * Compiles a string with markup into an interpolation function. This service is used by the - * HTML {@link ng.$compile $compile} service for data binding. See - * {@link ng.$interpolateProvider $interpolateProvider} for configuring the - * interpolation markup. - * - * -
-         var $interpolate = ...; // injected
-         var exp = $interpolate('Hello {{name}}!');
-         expect(exp({name:'Angular'}).toEqual('Hello Angular!');
-       
- * - * - * @param {string} text The text with markup to interpolate. - * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have - * embedded expression in order to return an interpolation function. Strings with no - * embedded expression will return null for the interpolation function. - * @returns {function(context)} an interpolation function which is used to compute the interpolated - * string. The function has these parameters: - * - * * `context`: an object against which any expressions embedded in the strings are evaluated - * against. - * - */ - function $interpolate(text, mustHaveExpression) { - var startIndex, - endIndex, - index = 0, - parts = [], - length = text.length, - hasInterpolation = false, - fn, - exp, - concat = []; - - while(index < length) { - if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) && - ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) { - (index != startIndex) && parts.push(text.substring(index, startIndex)); - parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex))); - fn.exp = exp; - index = endIndex + endSymbolLength; - hasInterpolation = true; - } else { - // we did not find anything, so we have to add the remainder to the parts array - (index != length) && parts.push(text.substring(index)); - index = length; - } - } - - if (!(length = parts.length)) { - // we added, nothing, must have been an empty string. - parts.push(''); - length = 1; - } - - if (!mustHaveExpression || hasInterpolation) { - concat.length = length; - fn = function(context) { - try { - for(var i = 0, ii = length, part; i=} search New search params - string or hash object - * @param {string=} paramValue If `search` is a string, then `paramValue` will override only a - * single search parameter. If the value is `null`, the parameter will be deleted. - * - * @return {string} search - */ - search: function(search, paramValue) { - if (isUndefined(search)) - return this.$$search; - - if (isDefined(paramValue)) { - if (paramValue === null) { - delete this.$$search[search]; - } else { - this.$$search[search] = paramValue; - } - } else { - this.$$search = isString(search) ? parseKeyValue(search) : search; - } - - this.$$compose(); - return this; - }, - - /** - * @ngdoc method - * @name ng.$location#hash - * @methodOf ng.$location - * - * @description - * This method is getter / setter. - * - * Return hash fragment when called without any parameter. - * - * Change hash fragment when called with parameter and return `$location`. - * - * @param {string=} hash New hash fragment - * @return {string} hash - */ - hash: locationGetterSetter('$$hash', identity), - - /** - * @ngdoc method - * @name ng.$location#replace - * @methodOf ng.$location - * - * @description - * If called, all changes to $location during current `$digest` will be replacing current history - * record, instead of adding new one. - */ - replace: function() { - this.$$replace = true; - return this; - } -}; - -function locationGetter(property) { - return function() { - return this[property]; - }; -} - - -function locationGetterSetter(property, preprocess) { - return function(value) { - if (isUndefined(value)) - return this[property]; - - this[property] = preprocess(value); - this.$$compose(); - - return this; - }; -} - - -/** - * @ngdoc object - * @name ng.$location - * - * @requires $browser - * @requires $sniffer - * @requires $rootElement - * - * @description - * The $location service parses the URL in the browser address bar (based on the - * {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL - * available to your application. Changes to the URL in the address bar are reflected into - * $location service and changes to $location are reflected into the browser address bar. - * - * **The $location service:** - * - * - Exposes the current URL in the browser address bar, so you can - * - Watch and observe the URL. - * - Change the URL. - * - Synchronizes the URL with the browser when the user - * - Changes the address bar. - * - Clicks the back or forward button (or clicks a History link). - * - Clicks on a link. - * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). - * - * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular - * Services: Using $location} - */ - -/** - * @ngdoc object - * @name ng.$locationProvider - * @description - * Use the `$locationProvider` to configure how the application deep linking paths are stored. - */ -function $LocationProvider(){ - var hashPrefix = '', - html5Mode = false; - - /** - * @ngdoc property - * @name ng.$locationProvider#hashPrefix - * @methodOf ng.$locationProvider - * @description - * @param {string=} prefix Prefix for hash part (containing path and search) - * @returns {*} current value if used as getter or itself (chaining) if used as setter - */ - this.hashPrefix = function(prefix) { - if (isDefined(prefix)) { - hashPrefix = prefix; - return this; - } else { - return hashPrefix; - } - }; - - /** - * @ngdoc property - * @name ng.$locationProvider#html5Mode - * @methodOf ng.$locationProvider - * @description - * @param {string=} mode Use HTML5 strategy if available. - * @returns {*} current value if used as getter or itself (chaining) if used as setter - */ - this.html5Mode = function(mode) { - if (isDefined(mode)) { - html5Mode = mode; - return this; - } else { - return html5Mode; - } - }; - - this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', - function( $rootScope, $browser, $sniffer, $rootElement) { - var $location, - LocationMode, - baseHref = $browser.baseHref(), - initialUrl = $browser.url(), - appBase; - - if (html5Mode) { - appBase = baseHref ? serverBase(initialUrl) + baseHref : initialUrl; - LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; - } else { - appBase = stripHash(initialUrl); - LocationMode = LocationHashbangUrl; - } - $location = new LocationMode(appBase, '#' + hashPrefix); - $location.$$parse($location.$$rewrite(initialUrl)); - - $rootElement.bind('click', function(event) { - // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) - // currently we open nice url link and redirect then - - if (event.ctrlKey || event.metaKey || event.which == 2) return; - - var elm = jqLite(event.target); - - // traverse the DOM up to find first A tag - while (lowercase(elm[0].nodeName) !== 'a') { - // ignore rewriting if no A tag (reached root element, or no parent - removed from document) - if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; - } - - var absHref = elm.prop('href'); - var rewrittenUrl = $location.$$rewrite(absHref); - - if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) { - event.preventDefault(); - if (rewrittenUrl != $browser.url()) { - // update location manually - $location.$$parse(rewrittenUrl); - $rootScope.$apply(); - // hack to work around FF6 bug 684208 when scenario runner clicks on links - window.angular['ff-684208-preventDefault'] = true; - } - } - }); - - - // rewrite hashbang url <> html5 url - if ($location.absUrl() != initialUrl) { - $browser.url($location.absUrl(), true); - } - - // update $location when $browser url changes - $browser.onUrlChange(function(newUrl) { - if ($location.absUrl() != newUrl) { - if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) { - $browser.url($location.absUrl()); - return; - } - $rootScope.$evalAsync(function() { - var oldUrl = $location.absUrl(); - - $location.$$parse(newUrl); - afterLocationChange(oldUrl); - }); - if (!$rootScope.$$phase) $rootScope.$digest(); - } - }); - - // update browser - var changeCounter = 0; - $rootScope.$watch(function $locationWatch() { - var oldUrl = $browser.url(); - var currentReplace = $location.$$replace; - - if (!changeCounter || oldUrl != $location.absUrl()) { - changeCounter++; - $rootScope.$evalAsync(function() { - if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). - defaultPrevented) { - $location.$$parse(oldUrl); - } else { - $browser.url($location.absUrl(), currentReplace); - afterLocationChange(oldUrl); - } - }); - } - $location.$$replace = false; - - return changeCounter; - }); - - return $location; - - function afterLocationChange(oldUrl) { - $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl); - } -}]; -} - -/** - * @ngdoc object - * @name ng.$log - * @requires $window - * - * @description - * Simple service for logging. Default implementation writes the message - * into the browser's console (if present). - * - * The main purpose of this service is to simplify debugging and troubleshooting. - * - * @example - - - function LogCtrl($scope, $log) { - $scope.$log = $log; - $scope.message = 'Hello World!'; - } - - -
-

Reload this page with open console, enter text and hit the log button...

- Message: - - - - - -
-
-
- */ - -/** - * @ngdoc object - * @name ng.$logProvider - * @description - * Use the `$logProvider` to configure how the application logs messages - */ -function $LogProvider(){ - var debug = true, - self = this; - - /** - * @ngdoc property - * @name ng.$logProvider#debugEnabled - * @methodOf ng.$logProvider - * @description - * @param {string=} flag enable or disable debug level messages - * @returns {*} current value if used as getter or itself (chaining) if used as setter - */ - this.debugEnabled = function(flag) { - if (isDefined(flag)) { - debug = flag; - return this; - } else { - return debug; - } - }; - - this.$get = ['$window', function($window){ - return { - /** - * @ngdoc method - * @name ng.$log#log - * @methodOf ng.$log - * - * @description - * Write a log message - */ - log: consoleLog('log'), - - /** - * @ngdoc method - * @name ng.$log#warn - * @methodOf ng.$log - * - * @description - * Write a warning message - */ - warn: consoleLog('warn'), - - /** - * @ngdoc method - * @name ng.$log#info - * @methodOf ng.$log - * - * @description - * Write an information message - */ - info: consoleLog('info'), - - /** - * @ngdoc method - * @name ng.$log#error - * @methodOf ng.$log - * - * @description - * Write an error message - */ - error: consoleLog('error'), - - /** - * @ngdoc method - * @name ng.$log#debug - * @methodOf ng.$log - * - * @description - * Write a debug message - */ - debug: (function () { - var fn = consoleLog('debug'); - - return function() { - if (debug) { - fn.apply(self, arguments); - } - } - }()) - }; - - function formatError(arg) { - if (arg instanceof Error) { - if (arg.stack) { - arg = (arg.message && arg.stack.indexOf(arg.message) === -1) - ? 'Error: ' + arg.message + '\n' + arg.stack - : arg.stack; - } else if (arg.sourceURL) { - arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; - } - } - return arg; - } - - function consoleLog(type) { - var console = $window.console || {}, - logFn = console[type] || console.log || noop; - - if (logFn.apply) { - return function() { - var args = []; - forEach(arguments, function(arg) { - args.push(formatError(arg)); - }); - return logFn.apply(console, args); - }; - } - - // we are IE which either doesn't have window.console => this is noop and we do nothing, - // or we are IE where console.log doesn't have apply so we log at least first 2 args - return function(arg1, arg2) { - logFn(arg1, arg2); - } - } - }]; -} - -var OPERATORS = { - 'null':function(){return null;}, - 'true':function(){return true;}, - 'false':function(){return false;}, - undefined:noop, - '+':function(self, locals, a,b){ - a=a(self, locals); b=b(self, locals); - if (isDefined(a)) { - if (isDefined(b)) { - return a + b; - } - return a; - } - return isDefined(b)?b:undefined;}, - '-':function(self, locals, a,b){a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)-(isDefined(b)?b:0);}, - '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, - '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, - '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, - '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, - '=':noop, - '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);}, - '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);}, - '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);}, - '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);}, - '<':function(self, locals, a,b){return a(self, locals)':function(self, locals, a,b){return a(self, locals)>b(self, locals);}, - '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);}, - '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);}, - '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);}, - '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);}, - '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);}, -// '|':function(self, locals, a,b){return a|b;}, - '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));}, - '!':function(self, locals, a){return !a(self, locals);} -}; -var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; - -function lex(text, csp){ - var tokens = [], - token, - index = 0, - json = [], - ch, - lastCh = ':'; // can start regexp - - while (index < text.length) { - ch = text.charAt(index); - if (is('"\'')) { - readString(ch); - } else if (isNumber(ch) || is('.') && isNumber(peek())) { - readNumber(); - } else if (isIdent(ch)) { - readIdent(); - // identifiers can only be if the preceding char was a { or , - if (was('{,') && json[0]=='{' && - (token=tokens[tokens.length-1])) { - token.json = token.text.indexOf('.') == -1; - } - } else if (is('(){}[].,;:?')) { - tokens.push({ - index:index, - text:ch, - json:(was(':[,') && is('{[')) || is('}]:,') - }); - if (is('{[')) json.unshift(ch); - if (is('}]')) json.shift(); - index++; - } else if (isWhitespace(ch)) { - index++; - continue; - } else { - var ch2 = ch + peek(), - ch3 = ch2 + peek(2), - fn = OPERATORS[ch], - fn2 = OPERATORS[ch2], - fn3 = OPERATORS[ch3]; - if (fn3) { - tokens.push({index:index, text:ch3, fn:fn3}); - index += 3; - } else if (fn2) { - tokens.push({index:index, text:ch2, fn:fn2}); - index += 2; - } else if (fn) { - tokens.push({index:index, text:ch, fn:fn, json: was('[,:') && is('+-')}); - index += 1; - } else { - throwError("Unexpected next character ", index, index+1); - } - } - lastCh = ch; - } - return tokens; - - function is(chars) { - return chars.indexOf(ch) != -1; - } - - function was(chars) { - return chars.indexOf(lastCh) != -1; - } - - function peek(i) { - var num = i || 1; - return index + num < text.length ? text.charAt(index + num) : false; - } - function isNumber(ch) { - return '0' <= ch && ch <= '9'; - } - function isWhitespace(ch) { - return ch == ' ' || ch == '\r' || ch == '\t' || - ch == '\n' || ch == '\v' || ch == '\u00A0'; // IE treats non-breaking space as \u00A0 - } - function isIdent(ch) { - return 'a' <= ch && ch <= 'z' || - 'A' <= ch && ch <= 'Z' || - '_' == ch || ch == '$'; - } - function isExpOperator(ch) { - return ch == '-' || ch == '+' || isNumber(ch); - } - - function throwError(error, start, end) { - end = end || index; - throw Error("Lexer Error: " + error + " at column" + - (isDefined(start) - ? "s " + start + "-" + index + " [" + text.substring(start, end) + "]" - : " " + end) + - " in expression [" + text + "]."); - } - - function readNumber() { - var number = ""; - var start = index; - while (index < text.length) { - var ch = lowercase(text.charAt(index)); - if (ch == '.' || isNumber(ch)) { - number += ch; - } else { - var peekCh = peek(); - if (ch == 'e' && isExpOperator(peekCh)) { - number += ch; - } else if (isExpOperator(ch) && - peekCh && isNumber(peekCh) && - number.charAt(number.length - 1) == 'e') { - number += ch; - } else if (isExpOperator(ch) && - (!peekCh || !isNumber(peekCh)) && - number.charAt(number.length - 1) == 'e') { - throwError('Invalid exponent'); - } else { - break; - } - } - index++; - } - number = 1 * number; - tokens.push({index:start, text:number, json:true, - fn:function() {return number;}}); - } - function readIdent() { - var ident = "", - start = index, - lastDot, peekIndex, methodName, ch; - - while (index < text.length) { - ch = text.charAt(index); - if (ch == '.' || isIdent(ch) || isNumber(ch)) { - if (ch == '.') lastDot = index; - ident += ch; - } else { - break; - } - index++; - } - - //check if this is not a method invocation and if it is back out to last dot - if (lastDot) { - peekIndex = index; - while(peekIndex < text.length) { - ch = text.charAt(peekIndex); - if (ch == '(') { - methodName = ident.substr(lastDot - start + 1); - ident = ident.substr(0, lastDot - start); - index = peekIndex; - break; - } - if(isWhitespace(ch)) { - peekIndex++; - } else { - break; - } - } - } - - - var token = { - index:start, - text:ident - }; - - if (OPERATORS.hasOwnProperty(ident)) { - token.fn = token.json = OPERATORS[ident]; - } else { - var getter = getterFn(ident, csp); - token.fn = extend(function(self, locals) { - return (getter(self, locals)); - }, { - assign: function(self, value) { - return setter(self, ident, value); - } - }); - } - - tokens.push(token); - - if (methodName) { - tokens.push({ - index:lastDot, - text: '.', - json: false - }); - tokens.push({ - index: lastDot + 1, - text: methodName, - json: false - }); - } - } - - function readString(quote) { - var start = index; - index++; - var string = ""; - var rawString = quote; - var escape = false; - while (index < text.length) { - var ch = text.charAt(index); - rawString += ch; - if (escape) { - if (ch == 'u') { - var hex = text.substring(index + 1, index + 5); - if (!hex.match(/[\da-f]{4}/i)) - throwError( "Invalid unicode escape [\\u" + hex + "]"); - index += 4; - string += String.fromCharCode(parseInt(hex, 16)); - } else { - var rep = ESCAPE[ch]; - if (rep) { - string += rep; - } else { - string += ch; - } - } - escape = false; - } else if (ch == '\\') { - escape = true; - } else if (ch == quote) { - index++; - tokens.push({ - index:start, - text:rawString, - string:string, - json:true, - fn:function() { return string; } - }); - return; - } else { - string += ch; - } - index++; - } - throwError("Unterminated quote", start); - } -} - -///////////////////////////////////////// - -function parser(text, json, $filter, csp){ - var ZERO = valueFn(0), - value, - tokens = lex(text, csp), - assignment = _assignment, - functionCall = _functionCall, - fieldAccess = _fieldAccess, - objectIndex = _objectIndex, - filterChain = _filterChain; - - if(json){ - // The extra level of aliasing is here, just in case the lexer misses something, so that - // we prevent any accidental execution in JSON. - assignment = logicalOR; - functionCall = - fieldAccess = - objectIndex = - filterChain = - function() { throwError("is not valid json", {text:text, index:0}); }; - value = primary(); - } else { - value = statements(); - } - if (tokens.length !== 0) { - throwError("is an unexpected token", tokens[0]); - } - value.literal = !!value.literal; - value.constant = !!value.constant; - return value; - - /////////////////////////////////// - function throwError(msg, token) { - throw Error("Syntax Error: Token '" + token.text + - "' " + msg + " at column " + - (token.index + 1) + " of the expression [" + - text + "] starting at [" + text.substring(token.index) + "]."); - } - - function peekToken() { - if (tokens.length === 0) - throw Error("Unexpected end of expression: " + text); - return tokens[0]; - } - - function peek(e1, e2, e3, e4) { - if (tokens.length > 0) { - var token = tokens[0]; - var t = token.text; - if (t==e1 || t==e2 || t==e3 || t==e4 || - (!e1 && !e2 && !e3 && !e4)) { - return token; - } - } - return false; - } - - function expect(e1, e2, e3, e4){ - var token = peek(e1, e2, e3, e4); - if (token) { - if (json && !token.json) { - throwError("is not valid json", token); - } - tokens.shift(); - return token; - } - return false; - } - - function consume(e1){ - if (!expect(e1)) { - throwError("is unexpected, expecting [" + e1 + "]", peek()); - } - } - - function unaryFn(fn, right) { - return extend(function(self, locals) { - return fn(self, locals, right); - }, { - constant:right.constant - }); - } - - function ternaryFn(left, middle, right){ - return extend(function(self, locals){ - return left(self, locals) ? middle(self, locals) : right(self, locals); - }, { - constant: left.constant && middle.constant && right.constant - }); - } - - function binaryFn(left, fn, right) { - return extend(function(self, locals) { - return fn(self, locals, left, right); - }, { - constant:left.constant && right.constant - }); - } - - function statements() { - var statements = []; - while(true) { - if (tokens.length > 0 && !peek('}', ')', ';', ']')) - statements.push(filterChain()); - if (!expect(';')) { - // optimize for the common case where there is only one statement. - // TODO(size): maybe we should not support multiple statements? - return statements.length == 1 - ? statements[0] - : function(self, locals){ - var value; - for ( var i = 0; i < statements.length; i++) { - var statement = statements[i]; - if (statement) - value = statement(self, locals); - } - return value; - }; - } - } - } - - function _filterChain() { - var left = expression(); - var token; - while(true) { - if ((token = expect('|'))) { - left = binaryFn(left, token.fn, filter()); - } else { - return left; - } - } - } - - function filter() { - var token = expect(); - var fn = $filter(token.text); - var argsFn = []; - while(true) { - if ((token = expect(':'))) { - argsFn.push(expression()); - } else { - var fnInvoke = function(self, locals, input){ - var args = [input]; - for ( var i = 0; i < argsFn.length; i++) { - args.push(argsFn[i](self, locals)); - } - return fn.apply(self, args); - }; - return function() { - return fnInvoke; - }; - } - } - } - - function expression() { - return assignment(); - } - - function _assignment() { - var left = ternary(); - var right; - var token; - if ((token = expect('='))) { - if (!left.assign) { - throwError("implies assignment but [" + - text.substring(0, token.index) + "] can not be assigned to", token); - } - right = ternary(); - return function(scope, locals){ - return left.assign(scope, right(scope, locals), locals); - }; - } else { - return left; - } - } - - function ternary() { - var left = logicalOR(); - var middle; - var token; - if((token = expect('?'))){ - middle = ternary(); - if((token = expect(':'))){ - return ternaryFn(left, middle, ternary()); - } - else { - throwError('expected :', token); - } - } - else { - return left; - } - } - - function logicalOR() { - var left = logicalAND(); - var token; - while(true) { - if ((token = expect('||'))) { - left = binaryFn(left, token.fn, logicalAND()); - } else { - return left; - } - } - } - - function logicalAND() { - var left = equality(); - var token; - if ((token = expect('&&'))) { - left = binaryFn(left, token.fn, logicalAND()); - } - return left; - } - - function equality() { - var left = relational(); - var token; - if ((token = expect('==','!=','===','!=='))) { - left = binaryFn(left, token.fn, equality()); - } - return left; - } - - function relational() { - var left = additive(); - var token; - if ((token = expect('<', '>', '<=', '>='))) { - left = binaryFn(left, token.fn, relational()); - } - return left; - } - - function additive() { - var left = multiplicative(); - var token; - while ((token = expect('+','-'))) { - left = binaryFn(left, token.fn, multiplicative()); - } - return left; - } - - function multiplicative() { - var left = unary(); - var token; - while ((token = expect('*','/','%'))) { - left = binaryFn(left, token.fn, unary()); - } - return left; - } - - function unary() { - var token; - if (expect('+')) { - return primary(); - } else if ((token = expect('-'))) { - return binaryFn(ZERO, token.fn, unary()); - } else if ((token = expect('!'))) { - return unaryFn(token.fn, unary()); - } else { - return primary(); - } - } - - - function primary() { - var primary; - if (expect('(')) { - primary = filterChain(); - consume(')'); - } else if (expect('[')) { - primary = arrayDeclaration(); - } else if (expect('{')) { - primary = object(); - } else { - var token = expect(); - primary = token.fn; - if (!primary) { - throwError("not a primary expression", token); - } - if (token.json) { - primary.constant = primary.literal = true; - } - } - - var next, context; - while ((next = expect('(', '[', '.'))) { - if (next.text === '(') { - primary = functionCall(primary, context); - context = null; - } else if (next.text === '[') { - context = primary; - primary = objectIndex(primary); - } else if (next.text === '.') { - context = primary; - primary = fieldAccess(primary); - } else { - throwError("IMPOSSIBLE"); - } - } - return primary; - } - - function _fieldAccess(object) { - var field = expect().text; - var getter = getterFn(field, csp); - return extend( - function(scope, locals, self) { - return getter(self || object(scope, locals), locals); - }, - { - assign:function(scope, value, locals) { - return setter(object(scope, locals), field, value); - } - } - ); - } - - function _objectIndex(obj) { - var indexFn = expression(); - consume(']'); - return extend( - function(self, locals){ - var o = obj(self, locals), - i = indexFn(self, locals), - v, p; - - if (!o) return undefined; - v = o[i]; - if (v && v.then) { - p = v; - if (!('$$v' in v)) { - p.$$v = undefined; - p.then(function(val) { p.$$v = val; }); - } - v = v.$$v; - } - return v; - }, { - assign:function(self, value, locals){ - return obj(self, locals)[indexFn(self, locals)] = value; - } - }); - } - - function _functionCall(fn, contextGetter) { - var argsFn = []; - if (peekToken().text != ')') { - do { - argsFn.push(expression()); - } while (expect(',')); - } - consume(')'); - return function(scope, locals){ - var args = [], - context = contextGetter ? contextGetter(scope, locals) : scope; - - for ( var i = 0; i < argsFn.length; i++) { - args.push(argsFn[i](scope, locals)); - } - var fnPtr = fn(scope, locals, context) || noop; - // IE stupidity! - return fnPtr.apply - ? fnPtr.apply(context, args) - : fnPtr(args[0], args[1], args[2], args[3], args[4]); - }; - } - - // This is used with json array declaration - function arrayDeclaration () { - var elementFns = []; - var allConstant = true; - if (peekToken().text != ']') { - do { - var elementFn = expression(); - elementFns.push(elementFn); - if (!elementFn.constant) { - allConstant = false; - } - } while (expect(',')); - } - consume(']'); - return extend(function(self, locals){ - var array = []; - for ( var i = 0; i < elementFns.length; i++) { - array.push(elementFns[i](self, locals)); - } - return array; - }, { - literal:true, - constant:allConstant - }); - } - - function object () { - var keyValues = []; - var allConstant = true; - if (peekToken().text != '}') { - do { - var token = expect(), - key = token.string || token.text; - consume(":"); - var value = expression(); - keyValues.push({key:key, value:value}); - if (!value.constant) { - allConstant = false; - } - } while (expect(',')); - } - consume('}'); - return extend(function(self, locals){ - var object = {}; - for ( var i = 0; i < keyValues.length; i++) { - var keyValue = keyValues[i]; - object[keyValue.key] = keyValue.value(self, locals); - } - return object; - }, { - literal:true, - constant:allConstant - }); - } -} - -////////////////////////////////////////////////// -// Parser helper functions -////////////////////////////////////////////////// - -function setter(obj, path, setValue) { - var element = path.split('.'); - for (var i = 0; element.length > 1; i++) { - var key = element.shift(); - var propertyObj = obj[key]; - if (!propertyObj) { - propertyObj = {}; - obj[key] = propertyObj; - } - obj = propertyObj; - } - obj[element.shift()] = setValue; - return setValue; -} - -/** - * Return the value accessible from the object by path. Any undefined traversals are ignored - * @param {Object} obj starting object - * @param {string} path path to traverse - * @param {boolean=true} bindFnToScope - * @returns value as accessible by path - */ -//TODO(misko): this function needs to be removed -function getter(obj, path, bindFnToScope) { - if (!path) return obj; - var keys = path.split('.'); - var key; - var lastInstance = obj; - var len = keys.length; - - for (var i = 0; i < len; i++) { - key = keys[i]; - if (obj) { - obj = (lastInstance = obj)[key]; - } - } - if (!bindFnToScope && isFunction(obj)) { - return bind(lastInstance, obj); - } - return obj; -} - -var getterFnCache = {}; - -/** - * Implementation of the "Black Hole" variant from: - * - http://jsperf.com/angularjs-parse-getter/4 - * - http://jsperf.com/path-evaluation-simplified/7 - */ -function cspSafeGetterFn(key0, key1, key2, key3, key4) { - return function(scope, locals) { - var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope, - promise; - - if (pathVal === null || pathVal === undefined) return pathVal; - - pathVal = pathVal[key0]; - if (pathVal && pathVal.then) { - if (!("$$v" in pathVal)) { - promise = pathVal; - promise.$$v = undefined; - promise.then(function(val) { promise.$$v = val; }); - } - pathVal = pathVal.$$v; - } - if (!key1 || pathVal === null || pathVal === undefined) return pathVal; - - pathVal = pathVal[key1]; - if (pathVal && pathVal.then) { - if (!("$$v" in pathVal)) { - promise = pathVal; - promise.$$v = undefined; - promise.then(function(val) { promise.$$v = val; }); - } - pathVal = pathVal.$$v; - } - if (!key2 || pathVal === null || pathVal === undefined) return pathVal; - - pathVal = pathVal[key2]; - if (pathVal && pathVal.then) { - if (!("$$v" in pathVal)) { - promise = pathVal; - promise.$$v = undefined; - promise.then(function(val) { promise.$$v = val; }); - } - pathVal = pathVal.$$v; - } - if (!key3 || pathVal === null || pathVal === undefined) return pathVal; - - pathVal = pathVal[key3]; - if (pathVal && pathVal.then) { - if (!("$$v" in pathVal)) { - promise = pathVal; - promise.$$v = undefined; - promise.then(function(val) { promise.$$v = val; }); - } - pathVal = pathVal.$$v; - } - if (!key4 || pathVal === null || pathVal === undefined) return pathVal; - - pathVal = pathVal[key4]; - if (pathVal && pathVal.then) { - if (!("$$v" in pathVal)) { - promise = pathVal; - promise.$$v = undefined; - promise.then(function(val) { promise.$$v = val; }); - } - pathVal = pathVal.$$v; - } - return pathVal; - }; -} - -function getterFn(path, csp) { - if (getterFnCache.hasOwnProperty(path)) { - return getterFnCache[path]; - } - - var pathKeys = path.split('.'), - pathKeysLength = pathKeys.length, - fn; - - if (csp) { - fn = (pathKeysLength < 6) - ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4]) - : function(scope, locals) { - var i = 0, val; - do { - val = cspSafeGetterFn( - pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++] - )(scope, locals); - - locals = undefined; // clear after first iteration - scope = val; - } while (i < pathKeysLength); - return val; - } - } else { - var code = 'var l, fn, p;\n'; - forEach(pathKeys, function(key, index) { - code += 'if(s === null || s === undefined) return s;\n' + - 'l=s;\n' + - 's='+ (index - // we simply dereference 's' on any .dot notation - ? 's' - // but if we are first then we check locals first, and if so read it first - : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' + - 'if (s && s.then) {\n' + - ' if (!("$$v" in s)) {\n' + - ' p=s;\n' + - ' p.$$v = undefined;\n' + - ' p.then(function(v) {p.$$v=v;});\n' + - '}\n' + - ' s=s.$$v\n' + - '}\n'; - }); - code += 'return s;'; - fn = Function('s', 'k', code); // s=scope, k=locals - fn.toString = function() { return code; }; - } - - return getterFnCache[path] = fn; -} - -/////////////////////////////////// - -/** - * @ngdoc function - * @name ng.$parse - * @function - * - * @description - * - * Converts Angular {@link guide/expression expression} into a function. - * - *
- *   var getter = $parse('user.name');
- *   var setter = getter.assign;
- *   var context = {user:{name:'angular'}};
- *   var locals = {user:{name:'local'}};
- *
- *   expect(getter(context)).toEqual('angular');
- *   setter(context, 'newValue');
- *   expect(context.user.name).toEqual('newValue');
- *   expect(getter(context, locals)).toEqual('local');
- * 
- * - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - * - * The returned function also has the following properties: - * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript - * literal. - * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript - * constant literals. - * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be - * set to a function to change its value on the given context. - * - */ -function $ParseProvider() { - var cache = {}; - this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { - return function(exp) { - switch(typeof exp) { - case 'string': - return cache.hasOwnProperty(exp) - ? cache[exp] - : cache[exp] = parser(exp, false, $filter, $sniffer.csp); - case 'function': - return exp; - default: - return noop; - } - }; - }]; -} - -/** - * @ngdoc service - * @name ng.$q - * @requires $rootScope - * - * @description - * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). - * - * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an - * interface for interacting with an object that represents the result of an action that is - * performed asynchronously, and may or may not be finished at any given point in time. - * - * From the perspective of dealing with error handling, deferred and promise APIs are to - * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. - * - *
- *   // for the purpose of this example let's assume that variables `$q` and `scope` are
- *   // available in the current lexical scope (they could have been injected or passed in).
- *
- *   function asyncGreet(name) {
- *     var deferred = $q.defer();
- *
- *     setTimeout(function() {
- *       // since this fn executes async in a future turn of the event loop, we need to wrap
- *       // our code into an $apply call so that the model changes are properly observed.
- *       scope.$apply(function() {
- *         if (okToGreet(name)) {
- *           deferred.resolve('Hello, ' + name + '!');
- *         } else {
- *           deferred.reject('Greeting ' + name + ' is not allowed.');
- *         }
- *       });
- *     }, 1000);
- *
- *     return deferred.promise;
- *   }
- *
- *   var promise = asyncGreet('Robin Hood');
- *   promise.then(function(greeting) {
- *     alert('Success: ' + greeting);
- *   }, function(reason) {
- *     alert('Failed: ' + reason);
- *   });
- * 
- * - * At first it might not be obvious why this extra complexity is worth the trouble. The payoff - * comes in the way of - * [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md). - * - * Additionally the promise api allows for composition that is very hard to do with the - * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. - * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the - * section on serial or parallel joining of promises. - * - * - * # The Deferred API - * - * A new instance of deferred is constructed by calling `$q.defer()`. - * - * The purpose of the deferred object is to expose the associated Promise instance as well as APIs - * that can be used for signaling the successful or unsuccessful completion of the task. - * - * **Methods** - * - * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection - * constructed via `$q.reject`, the promise will be rejected instead. - * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to - * resolving it with a rejection constructed via `$q.reject`. - * - * **Properties** - * - * - promise – `{Promise}` – promise object associated with this deferred. - * - * - * # The Promise API - * - * A new promise instance is created when a deferred instance is created and can be retrieved by - * calling `deferred.promise`. - * - * The purpose of the promise object is to allow for interested parties to get access to the result - * of the deferred task when it completes. - * - * **Methods** - * - * - `then(successCallback, errorCallback)` – regardless of when the promise was or will be resolved - * or rejected calls one of the success or error callbacks asynchronously as soon as the result - * is available. The callbacks are called with a single argument the result or rejection reason. - * - * This method *returns a new promise* which is resolved or rejected via the return value of the - * `successCallback` or `errorCallback`. - * - * - `always(callback)` – allows you to observe either the fulfillment or rejection of a promise, - * but to do so without modifying the final value. This is useful to release resources or do some - * clean-up that needs to be done whether the promise was rejected or resolved. See the [full - * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for - * more information. - * - * # Chaining promises - * - * Because calling `then` api of a promise returns a new derived promise, it is easily possible - * to create a chain of promises: - * - *
- *   promiseB = promiseA.then(function(result) {
- *     return result + 1;
- *   });
- *
- *   // promiseB will be resolved immediately after promiseA is resolved and its value will be
- *   // the result of promiseA incremented by 1
- * 
- * - * It is possible to create chains of any length and since a promise can be resolved with another - * promise (which will defer its resolution further), it is possible to pause/defer resolution of - * the promises at any point in the chain. This makes it possible to implement powerful apis like - * $http's response interceptors. - * - * - * # Differences between Kris Kowal's Q and $q - * - * There are three main differences: - * - * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation - * mechanism in angular, which means faster propagation of resolution or rejection into your - * models and avoiding unnecessary browser repaints, which would result in flickering UI. - * - $q promises are recognized by the templating engine in angular, which means that in templates - * you can treat promises attached to a scope as if they were the resulting values. - * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains - * all the important functionality needed for common async tasks. - * - * # Testing - * - *
- *    it('should simulate promise', inject(function($q, $rootScope) {
- *      var deferred = $q.defer();
- *      var promise = deferred.promise;
- *      var resolvedValue;
- * 
- *      promise.then(function(value) { resolvedValue = value; });
- *      expect(resolvedValue).toBeUndefined();
- * 
- *      // Simulate resolving of promise
- *      deferred.resolve(123);
- *      // Note that the 'then' function does not get called synchronously.
- *      // This is because we want the promise API to always be async, whether or not
- *      // it got called synchronously or asynchronously.
- *      expect(resolvedValue).toBeUndefined();
- * 
- *      // Propagate promise resolution to 'then' functions using $apply().
- *      $rootScope.$apply();
- *      expect(resolvedValue).toEqual(123);
- *    });
- *  
- */ -function $QProvider() { - - this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { - return qFactory(function(callback) { - $rootScope.$evalAsync(callback); - }, $exceptionHandler); - }]; -} - - -/** - * Constructs a promise manager. - * - * @param {function(function)} nextTick Function for executing functions in the next turn. - * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for - * debugging purposes. - * @returns {object} Promise manager. - */ -function qFactory(nextTick, exceptionHandler) { - - /** - * @ngdoc - * @name ng.$q#defer - * @methodOf ng.$q - * @description - * Creates a `Deferred` object which represents a task which will finish in the future. - * - * @returns {Deferred} Returns a new instance of deferred. - */ - var defer = function() { - var pending = [], - value, deferred; - - deferred = { - - resolve: function(val) { - if (pending) { - var callbacks = pending; - pending = undefined; - value = ref(val); - - if (callbacks.length) { - nextTick(function() { - var callback; - for (var i = 0, ii = callbacks.length; i < ii; i++) { - callback = callbacks[i]; - value.then(callback[0], callback[1]); - } - }); - } - } - }, - - - reject: function(reason) { - deferred.resolve(reject(reason)); - }, - - - promise: { - then: function(callback, errback) { - var result = defer(); - - var wrappedCallback = function(value) { - try { - result.resolve((callback || defaultCallback)(value)); - } catch(e) { - exceptionHandler(e); - result.reject(e); - } - }; - - var wrappedErrback = function(reason) { - try { - result.resolve((errback || defaultErrback)(reason)); - } catch(e) { - exceptionHandler(e); - result.reject(e); - } - }; - - if (pending) { - pending.push([wrappedCallback, wrappedErrback]); - } else { - value.then(wrappedCallback, wrappedErrback); - } - - return result.promise; - }, - always: function(callback) { - - function makePromise(value, resolved) { - var result = defer(); - if (resolved) { - result.resolve(value); - } else { - result.reject(value); - } - return result.promise; - } - - function handleCallback(value, isResolved) { - var callbackOutput = null; - try { - callbackOutput = (callback ||defaultCallback)(); - } catch(e) { - return makePromise(e, false); - } - if (callbackOutput && callbackOutput.then) { - return callbackOutput.then(function() { - return makePromise(value, isResolved); - }, function(error) { - return makePromise(error, false); - }); - } else { - return makePromise(value, isResolved); - } - } - - return this.then(function(value) { - return handleCallback(value, true); - }, function(error) { - return handleCallback(error, false); - }); - } - } - }; - - return deferred; - }; - - - var ref = function(value) { - if (value && value.then) return value; - return { - then: function(callback) { - var result = defer(); - nextTick(function() { - result.resolve(callback(value)); - }); - return result.promise; - } - }; - }; - - - /** - * @ngdoc - * @name ng.$q#reject - * @methodOf ng.$q - * @description - * Creates a promise that is resolved as rejected with the specified `reason`. This api should be - * used to forward rejection in a chain of promises. If you are dealing with the last promise in - * a promise chain, you don't need to worry about it. - * - * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of - * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via - * a promise error callback and you want to forward the error to the promise derived from the - * current promise, you have to "rethrow" the error by returning a rejection constructed via - * `reject`. - * - *
-   *   promiseB = promiseA.then(function(result) {
-   *     // success: do something and resolve promiseB
-   *     //          with the old or a new result
-   *     return result;
-   *   }, function(reason) {
-   *     // error: handle the error if possible and
-   *     //        resolve promiseB with newPromiseOrValue,
-   *     //        otherwise forward the rejection to promiseB
-   *     if (canHandle(reason)) {
-   *      // handle the error and recover
-   *      return newPromiseOrValue;
-   *     }
-   *     return $q.reject(reason);
-   *   });
-   * 
- * - * @param {*} reason Constant, message, exception or an object representing the rejection reason. - * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. - */ - var reject = function(reason) { - return { - then: function(callback, errback) { - var result = defer(); - nextTick(function() { - result.resolve((errback || defaultErrback)(reason)); - }); - return result.promise; - } - }; - }; - - - /** - * @ngdoc - * @name ng.$q#when - * @methodOf ng.$q - * @description - * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. - * This is useful when you are dealing with an object that might or might not be a promise, or if - * the promise comes from a source that can't be trusted. - * - * @param {*} value Value or a promise - * @returns {Promise} Returns a promise of the passed value or promise - */ - var when = function(value, callback, errback) { - var result = defer(), - done; - - var wrappedCallback = function(value) { - try { - return (callback || defaultCallback)(value); - } catch (e) { - exceptionHandler(e); - return reject(e); - } - }; - - var wrappedErrback = function(reason) { - try { - return (errback || defaultErrback)(reason); - } catch (e) { - exceptionHandler(e); - return reject(e); - } - }; - - nextTick(function() { - ref(value).then(function(value) { - if (done) return; - done = true; - result.resolve(ref(value).then(wrappedCallback, wrappedErrback)); - }, function(reason) { - if (done) return; - done = true; - result.resolve(wrappedErrback(reason)); - }); - }); - - return result.promise; - }; - - - function defaultCallback(value) { - return value; - } - - - function defaultErrback(reason) { - return reject(reason); - } - - - /** - * @ngdoc - * @name ng.$q#all - * @methodOf ng.$q - * @description - * Combines multiple promises into a single promise that is resolved when all of the input - * promises are resolved. - * - * @param {Array.|Object.} promises An array or hash of promises. - * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, - * each value corresponding to the promise at the same index/key in the `promises` array/hash. If any of - * the promises is resolved with a rejection, this resulting promise will be resolved with the - * same rejection. - */ - function all(promises) { - var deferred = defer(), - counter = 0, - results = isArray(promises) ? [] : {}; - - forEach(promises, function(promise, key) { - counter++; - ref(promise).then(function(value) { - if (results.hasOwnProperty(key)) return; - results[key] = value; - if (!(--counter)) deferred.resolve(results); - }, function(reason) { - if (results.hasOwnProperty(key)) return; - deferred.reject(reason); - }); - }); - - if (counter === 0) { - deferred.resolve(results); - } - - return deferred.promise; - } - - return { - defer: defer, - reject: reject, - when: when, - all: all - }; -} - -/** - * @ngdoc object - * @name ng.$routeProvider - * @function - * - * @description - * - * Used for configuring routes. See {@link ng.$route $route} for an example. - */ -function $RouteProvider(){ - var routes = {}; - - /** - * @ngdoc method - * @name ng.$routeProvider#when - * @methodOf ng.$routeProvider - * - * @param {string} path Route path (matched against `$location.path`). If `$location.path` - * contains redundant trailing slash or is missing one, the route will still match and the - * `$location.path` will be updated to add or drop the trailing slash to exactly match the - * route definition. - * - * * `path` can contain named groups starting with a colon (`:name`). All characters up - * to the next slash are matched and stored in `$routeParams` under the given `name` - * when the route matches. - * * `path` can contain named groups starting with a star (`*name`). All characters are - * eagerly stored in `$routeParams` under the given `name` when the route matches. - * - * For example, routes like `/color/:color/largecode/*largecode/edit` will match - * `/color/brown/largecode/code/with/slashs/edit` and extract: - * - * * `color: brown` - * * `largecode: code/with/slashs`. - * - * - * @param {Object} route Mapping information to be assigned to `$route.current` on route - * match. - * - * Object properties: - * - * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly - * created scope or the name of a {@link angular.Module#controller registered controller} - * if passed as a string. - * - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be - * published to scope under the `controllerAs` name. - * - `template` – `{string=|function()=}` – html template as a string or function that returns - * an html template as a string which should be used by {@link ng.directive:ngView ngView} or - * {@link ng.directive:ngInclude ngInclude} directives. - * This property takes precedence over `templateUrl`. - * - * If `template` is a function, it will be called with the following parameters: - * - * - `{Array.}` - route parameters extracted from the current - * `$location.path()` by applying the current route - * - * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html - * template that should be used by {@link ng.directive:ngView ngView}. - * - * If `templateUrl` is a function, it will be called with the following parameters: - * - * - `{Array.}` - route parameters extracted from the current - * `$location.path()` by applying the current route - * - * - `resolve` - `{Object.=}` - An optional map of dependencies which should - * be injected into the controller. If any of these dependencies are promises, they will be - * resolved and converted to a value before the controller is instantiated and the - * `$routeChangeSuccess` event is fired. The map object is: - * - * - `key` – `{string}`: a name of a dependency to be injected into the controller. - * - `factory` - `{string|function}`: If `string` then it is an alias for a service. - * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected} - * and the return value is treated as the dependency. If the result is a promise, it is resolved - * before its value is injected into the controller. - * - * - `redirectTo` – {(string|function())=} – value to update - * {@link ng.$location $location} path with and trigger route redirection. - * - * If `redirectTo` is a function, it will be called with the following parameters: - * - * - `{Object.}` - route parameters extracted from the current - * `$location.path()` by applying the current route templateUrl. - * - `{string}` - current `$location.path()` - * - `{Object}` - current `$location.search()` - * - * The custom `redirectTo` function is expected to return a string which will be used - * to update `$location.path()` and `$location.search()`. - * - * - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search() - * changes. - * - * If the option is set to `false` and url in the browser changes, then - * `$routeUpdate` event is broadcasted on the root scope. - * - * - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive - * - * If the option is set to `true`, then the particular route can be matched without being - * case sensitive - * - * @returns {Object} self - * - * @description - * Adds a new route definition to the `$route` service. - */ - this.when = function(path, route) { - routes[path] = extend({reloadOnSearch: true, caseInsensitiveMatch: false}, route); - - // create redirection for trailing slashes - if (path) { - var redirectPath = (path[path.length-1] == '/') - ? path.substr(0, path.length-1) - : path +'/'; - - routes[redirectPath] = {redirectTo: path}; - } - - return this; - }; - - /** - * @ngdoc method - * @name ng.$routeProvider#otherwise - * @methodOf ng.$routeProvider - * - * @description - * Sets route definition that will be used on route change when no other route definition - * is matched. - * - * @param {Object} params Mapping information to be assigned to `$route.current`. - * @returns {Object} self - */ - this.otherwise = function(params) { - this.when(null, params); - return this; - }; - - - this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache', - function( $rootScope, $location, $routeParams, $q, $injector, $http, $templateCache) { - - /** - * @ngdoc object - * @name ng.$route - * @requires $location - * @requires $routeParams - * - * @property {Object} current Reference to the current route definition. - * The route definition contains: - * - * - `controller`: The controller constructor as define in route definition. - * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for - * controller instantiation. The `locals` contain - * the resolved values of the `resolve` map. Additionally the `locals` also contain: - * - * - `$scope` - The current route scope. - * - `$template` - The current route template HTML. - * - * @property {Array.} routes Array of all configured routes. - * - * @description - * Is used for deep-linking URLs to controllers and views (HTML partials). - * It watches `$location.url()` and tries to map the path to an existing route definition. - * - * You can define routes through {@link ng.$routeProvider $routeProvider}'s API. - * - * The `$route` service is typically used in conjunction with {@link ng.directive:ngView ngView} - * directive and the {@link ng.$routeParams $routeParams} service. - * - * @example - This example shows how changing the URL hash causes the `$route` to match a route against the - URL, and the `ngView` pulls in the partial. - - Note that this example is using {@link ng.directive:script inlined templates} - to get it working on jsfiddle as well. - - - -
- Choose: - Moby | - Moby: Ch1 | - Gatsby | - Gatsby: Ch4 | - Scarlet Letter
- -
-
- -
$location.path() = {{$location.path()}}
-
$route.current.templateUrl = {{$route.current.templateUrl}}
-
$route.current.params = {{$route.current.params}}
-
$route.current.scope.name = {{$route.current.scope.name}}
-
$routeParams = {{$routeParams}}
-
-
- - - controller: {{name}}
- Book Id: {{params.bookId}}
-
- - - controller: {{name}}
- Book Id: {{params.bookId}}
- Chapter Id: {{params.chapterId}} -
- - - angular.module('ngView', [], function($routeProvider, $locationProvider) { - $routeProvider.when('/Book/:bookId', { - templateUrl: 'book.html', - controller: BookCntl, - resolve: { - // I will cause a 1 second delay - delay: function($q, $timeout) { - var delay = $q.defer(); - $timeout(delay.resolve, 1000); - return delay.promise; - } - } - }); - $routeProvider.when('/Book/:bookId/ch/:chapterId', { - templateUrl: 'chapter.html', - controller: ChapterCntl - }); - - // configure html5 to get links working on jsfiddle - $locationProvider.html5Mode(true); - }); - - function MainCntl($scope, $route, $routeParams, $location) { - $scope.$route = $route; - $scope.$location = $location; - $scope.$routeParams = $routeParams; - } - - function BookCntl($scope, $routeParams) { - $scope.name = "BookCntl"; - $scope.params = $routeParams; - } - - function ChapterCntl($scope, $routeParams) { - $scope.name = "ChapterCntl"; - $scope.params = $routeParams; - } - - - - it('should load and compile correct template', function() { - element('a:contains("Moby: Ch1")').click(); - var content = element('.doc-example-live [ng-view]').text(); - expect(content).toMatch(/controller\: ChapterCntl/); - expect(content).toMatch(/Book Id\: Moby/); - expect(content).toMatch(/Chapter Id\: 1/); - - element('a:contains("Scarlet")').click(); - sleep(2); // promises are not part of scenario waiting - content = element('.doc-example-live [ng-view]').text(); - expect(content).toMatch(/controller\: BookCntl/); - expect(content).toMatch(/Book Id\: Scarlet/); - }); - -
- */ - - /** - * @ngdoc event - * @name ng.$route#$routeChangeStart - * @eventOf ng.$route - * @eventType broadcast on root scope - * @description - * Broadcasted before a route change. At this point the route services starts - * resolving all of the dependencies needed for the route change to occurs. - * Typically this involves fetching the view template as well as any dependencies - * defined in `resolve` route property. Once all of the dependencies are resolved - * `$routeChangeSuccess` is fired. - * - * @param {Route} next Future route information. - * @param {Route} current Current route information. - */ - - /** - * @ngdoc event - * @name ng.$route#$routeChangeSuccess - * @eventOf ng.$route - * @eventType broadcast on root scope - * @description - * Broadcasted after a route dependencies are resolved. - * {@link ng.directive:ngView ngView} listens for the directive - * to instantiate the controller and render the view. - * - * @param {Object} angularEvent Synthetic event object. - * @param {Route} current Current route information. - * @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered. - */ - - /** - * @ngdoc event - * @name ng.$route#$routeChangeError - * @eventOf ng.$route - * @eventType broadcast on root scope - * @description - * Broadcasted if any of the resolve promises are rejected. - * - * @param {Route} current Current route information. - * @param {Route} previous Previous route information. - * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise. - */ - - /** - * @ngdoc event - * @name ng.$route#$routeUpdate - * @eventOf ng.$route - * @eventType broadcast on root scope - * @description - * - * The `reloadOnSearch` property has been set to false, and we are reusing the same - * instance of the Controller. - */ - - var forceReload = false, - $route = { - routes: routes, - - /** - * @ngdoc method - * @name ng.$route#reload - * @methodOf ng.$route - * - * @description - * Causes `$route` service to reload the current route even if - * {@link ng.$location $location} hasn't changed. - * - * As a result of that, {@link ng.directive:ngView ngView} - * creates new scope, reinstantiates the controller. - */ - reload: function() { - forceReload = true; - $rootScope.$evalAsync(updateRoute); - } - }; - - $rootScope.$on('$locationChangeSuccess', updateRoute); - - return $route; - - ///////////////////////////////////////////////////// - - /** - * @param on {string} current url - * @param when {string} route when template to match the url against - * @param whenProperties {Object} properties to define when's matching behavior - * @return {?Object} - */ - function switchRouteMatcher(on, when, whenProperties) { - // TODO(i): this code is convoluted and inefficient, we should construct the route matching - // regex only once and then reuse it - - // Escape regexp special characters. - when = '^' + when.replace(/[-\/\\^$:*+?.()|[\]{}]/g, "\\$&") + '$'; - - var regex = '', - params = [], - dst = {}; - - var re = /\\([:*])(\w+)/g, - paramMatch, - lastMatchedIndex = 0; - - while ((paramMatch = re.exec(when)) !== null) { - // Find each :param in `when` and replace it with a capturing group. - // Append all other sections of when unchanged. - regex += when.slice(lastMatchedIndex, paramMatch.index); - switch(paramMatch[1]) { - case ':': - regex += '([^\\/]*)'; - break; - case '*': - regex += '(.*)'; - break; - } - params.push(paramMatch[2]); - lastMatchedIndex = re.lastIndex; - } - // Append trailing path part. - regex += when.substr(lastMatchedIndex); - - var match = on.match(new RegExp(regex, whenProperties.caseInsensitiveMatch ? 'i' : '')); - if (match) { - forEach(params, function(name, index) { - dst[name] = match[index + 1]; - }); - } - return match ? dst : null; - } - - function updateRoute() { - var next = parseRoute(), - last = $route.current; - - if (next && last && next.$$route === last.$$route - && equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) { - last.params = next.params; - copy(last.params, $routeParams); - $rootScope.$broadcast('$routeUpdate', last); - } else if (next || last) { - forceReload = false; - $rootScope.$broadcast('$routeChangeStart', next, last); - $route.current = next; - if (next) { - if (next.redirectTo) { - if (isString(next.redirectTo)) { - $location.path(interpolate(next.redirectTo, next.params)).search(next.params) - .replace(); - } else { - $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search())) - .replace(); - } - } - } - - $q.when(next). - then(function() { - if (next) { - var locals = extend({}, next.resolve), - template; - - forEach(locals, function(value, key) { - locals[key] = isString(value) ? $injector.get(value) : $injector.invoke(value); - }); - - if (isDefined(template = next.template)) { - if (isFunction(template)) { - template = template(next.params); - } - } else if (isDefined(template = next.templateUrl)) { - if (isFunction(template)) { - template = template(next.params); - } - if (isDefined(template)) { - next.loadedTemplateUrl = template; - template = $http.get(template, {cache: $templateCache}). - then(function(response) { return response.data; }); - } - } - if (isDefined(template)) { - locals['$template'] = template; - } - return $q.all(locals); - } - }). - // after route change - then(function(locals) { - if (next == $route.current) { - if (next) { - next.locals = locals; - copy(next.params, $routeParams); - } - $rootScope.$broadcast('$routeChangeSuccess', next, last); - } - }, function(error) { - if (next == $route.current) { - $rootScope.$broadcast('$routeChangeError', next, last, error); - } - }); - } - } - - - /** - * @returns the current active route, by matching it against the URL - */ - function parseRoute() { - // Match a route - var params, match; - forEach(routes, function(route, path) { - if (!match && (params = switchRouteMatcher($location.path(), path, route))) { - match = inherit(route, { - params: extend({}, $location.search(), params), - pathParams: params}); - match.$$route = route; - } - }); - // No route matched; fallback to "otherwise" route - return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}}); - } - - /** - * @returns interpolation of the redirect path with the parameters - */ - function interpolate(string, params) { - var result = []; - forEach((string||'').split(':'), function(segment, i) { - if (i == 0) { - result.push(segment); - } else { - var segmentMatch = segment.match(/(\w+)(.*)/); - var key = segmentMatch[1]; - result.push(params[key]); - result.push(segmentMatch[2] || ''); - delete params[key]; - } - }); - return result.join(''); - } - }]; -} - -/** - * @ngdoc object - * @name ng.$routeParams - * @requires $route - * - * @description - * Current set of route parameters. The route parameters are a combination of the - * {@link ng.$location $location} `search()`, and `path()`. The `path` parameters - * are extracted when the {@link ng.$route $route} path is matched. - * - * In case of parameter name collision, `path` params take precedence over `search` params. - * - * The service guarantees that the identity of the `$routeParams` object will remain unchanged - * (but its properties will likely change) even when a route change occurs. - * - * @example - *
- *  // Given:
- *  // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
- *  // Route: /Chapter/:chapterId/Section/:sectionId
- *  //
- *  // Then
- *  $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
- * 
- */ -function $RouteParamsProvider() { - this.$get = valueFn({}); -} - -/** - * DESIGN NOTES - * - * The design decisions behind the scope are heavily favored for speed and memory consumption. - * - * The typical use of scope is to watch the expressions, which most of the time return the same - * value as last time so we optimize the operation. - * - * Closures construction is expensive in terms of speed as well as memory: - * - No closures, instead use prototypical inheritance for API - * - Internal state needs to be stored on scope directly, which means that private state is - * exposed as $$____ properties - * - * Loop operations are optimized by using while(count--) { ... } - * - this means that in order to keep the same order of execution as addition we have to add - * items to the array at the beginning (shift) instead of at the end (push) - * - * Child scopes are created and removed often - * - Using an array would be slow since inserts in middle are expensive so we use linked list - * - * There are few watches then a lot of observers. This is why you don't want the observer to be - * implemented in the same way as watch. Watch requires return of initialization function which - * are expensive to construct. - */ - - -/** - * @ngdoc object - * @name ng.$rootScopeProvider - * @description - * - * Provider for the $rootScope service. - */ - -/** - * @ngdoc function - * @name ng.$rootScopeProvider#digestTtl - * @methodOf ng.$rootScopeProvider - * @description - * - * Sets the number of digest iterations the scope should attempt to execute before giving up and - * assuming that the model is unstable. - * - * The current default is 10 iterations. - * - * @param {number} limit The number of digest iterations. - */ - - -/** - * @ngdoc object - * @name ng.$rootScope - * @description - * - * Every application has a single root {@link ng.$rootScope.Scope scope}. - * All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide - * event processing life-cycle. See {@link guide/scope developer guide on scopes}. - */ -function $RootScopeProvider(){ - var TTL = 10; - - this.digestTtl = function(value) { - if (arguments.length) { - TTL = value; - } - return TTL; - }; - - this.$get = ['$injector', '$exceptionHandler', '$parse', - function( $injector, $exceptionHandler, $parse) { - - /** - * @ngdoc function - * @name ng.$rootScope.Scope - * - * @description - * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the - * {@link AUTO.$injector $injector}. Child scopes are created using the - * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when - * compiled HTML template is executed.) - * - * Here is a simple scope snippet to show how you can interact with the scope. - *
-     * 
-     * 
- * - * # Inheritance - * A scope can inherit from a parent scope, as in this example: - *
-         var parent = $rootScope;
-         var child = parent.$new();
-
-         parent.salutation = "Hello";
-         child.name = "World";
-         expect(child.salutation).toEqual('Hello');
-
-         child.salutation = "Welcome";
-         expect(child.salutation).toEqual('Welcome');
-         expect(parent.salutation).toEqual('Hello');
-     * 
- * - * - * @param {Object.=} providers Map of service factory which need to be provided - * for the current scope. Defaults to {@link ng}. - * @param {Object.=} instanceCache Provides pre-instantiated services which should - * append/override services provided by `providers`. This is handy when unit-testing and having - * the need to override a default service. - * @returns {Object} Newly created scope. - * - */ - function Scope() { - this.$id = nextUid(); - this.$$phase = this.$parent = this.$$watchers = - this.$$nextSibling = this.$$prevSibling = - this.$$childHead = this.$$childTail = null; - this['this'] = this.$root = this; - this.$$destroyed = false; - this.$$asyncQueue = []; - this.$$listeners = {}; - this.$$isolateBindings = {}; - } - - /** - * @ngdoc property - * @name ng.$rootScope.Scope#$id - * @propertyOf ng.$rootScope.Scope - * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for - * debugging. - */ - - - Scope.prototype = { - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$new - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Creates a new child {@link ng.$rootScope.Scope scope}. - * - * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and - * {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the scope - * hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. - * - * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is desired for - * the scope and its child scopes to be permanently detached from the parent and thus stop - * participating in model change detection and listener notification by invoking. - * - * @param {boolean} isolate if true then the scope does not prototypically inherit from the - * parent scope. The scope is isolated, as it can not see parent scope properties. - * When creating widgets it is useful for the widget to not accidentally read parent - * state. - * - * @returns {Object} The newly created child scope. - * - */ - $new: function(isolate) { - var Child, - child; - - if (isFunction(isolate)) { - // TODO: remove at some point - throw Error('API-CHANGE: Use $controller to instantiate controllers.'); - } - if (isolate) { - child = new Scope(); - child.$root = this.$root; - } else { - Child = function() {}; // should be anonymous; This is so that when the minifier munges - // the name it does not become random set of chars. These will then show up as class - // name in the debugger. - Child.prototype = this; - child = new Child(); - child.$id = nextUid(); - } - child['this'] = child; - child.$$listeners = {}; - child.$parent = this; - child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null; - child.$$prevSibling = this.$$childTail; - if (this.$$childHead) { - this.$$childTail.$$nextSibling = child; - this.$$childTail = child; - } else { - this.$$childHead = this.$$childTail = child; - } - return child; - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$watch - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Registers a `listener` callback to be executed whenever the `watchExpression` changes. - * - * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest $digest()} and - * should return the value which will be watched. (Since {@link ng.$rootScope.Scope#$digest $digest()} - * reruns when it detects changes the `watchExpression` can execute multiple times per - * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) - * - The `listener` is called only when the value from the current `watchExpression` and the - * previous call to `watchExpression` are not equal (with the exception of the initial run, - * see below). The inequality is determined according to - * {@link angular.equals} function. To save the value of the object for later comparison, the - * {@link angular.copy} function is used. It also means that watching complex options will - * have adverse memory and performance implications. - * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This - * is achieved by rerunning the watchers until no changes are detected. The rerun iteration - * limit is 10 to prevent an infinite loop deadlock. - * - * - * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, - * you can register a `watchExpression` function with no `listener`. (Since `watchExpression` - * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a change is - * detected, be prepared for multiple calls to your listener.) - * - * After a watcher is registered with the scope, the `listener` fn is called asynchronously - * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the - * watcher. In rare cases, this is undesirable because the listener is called when the result - * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you - * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the - * listener was called due to initialization. - * - * - * # Example - *
-           // let's assume that scope was dependency injected as the $rootScope
-           var scope = $rootScope;
-           scope.name = 'misko';
-           scope.counter = 0;
-
-           expect(scope.counter).toEqual(0);
-           scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; });
-           expect(scope.counter).toEqual(0);
-
-           scope.$digest();
-           // no variable change
-           expect(scope.counter).toEqual(0);
-
-           scope.name = 'adam';
-           scope.$digest();
-           expect(scope.counter).toEqual(1);
-       * 
- * - * - * - * @param {(function()|string)} watchExpression Expression that is evaluated on each - * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a - * call to the `listener`. - * - * - `string`: Evaluated as {@link guide/expression expression} - * - `function(scope)`: called with current `scope` as a parameter. - * @param {(function()|string)=} listener Callback called whenever the return value of - * the `watchExpression` changes. - * - * - `string`: Evaluated as {@link guide/expression expression} - * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters. - * - * @param {boolean=} objectEquality Compare object for equality rather than for reference. - * @returns {function()} Returns a deregistration function for this listener. - */ - $watch: function(watchExp, listener, objectEquality) { - var scope = this, - get = compileToFn(watchExp, 'watch'), - array = scope.$$watchers, - watcher = { - fn: listener, - last: initWatchVal, - get: get, - exp: watchExp, - eq: !!objectEquality - }; - - // in the case user pass string, we need to compile it, do we really need this ? - if (!isFunction(listener)) { - var listenFn = compileToFn(listener || noop, 'listener'); - watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);}; - } - - if (typeof watchExp == 'string' && get.constant) { - var originalFn = watcher.fn; - watcher.fn = function(newVal, oldVal, scope) { - originalFn.call(this, newVal, oldVal, scope); - arrayRemove(array, watcher); - }; - } - - if (!array) { - array = scope.$$watchers = []; - } - // we use unshift since we use a while loop in $digest for speed. - // the while loop reads in reverse order. - array.unshift(watcher); - - return function() { - arrayRemove(array, watcher); - }; - }, - - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$watchCollection - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Shallow watches the properties of an object and fires whenever any of the properties change - * (for arrays this implies watching the array items, for object maps this implies watching the properties). - * If a change is detected the `listener` callback is fired. - * - * - The `obj` collection is observed via standard $watch operation and is examined on every call to $digest() to - * see if any items have been added, removed, or moved. - * - The `listener` is called whenever anything within the `obj` has changed. Examples include adding new items - * into the object or array, removing and moving items around. - * - * - * # Example - *
-          $scope.names = ['igor', 'matias', 'misko', 'james'];
-          $scope.dataCount = 4;
-
-          $scope.$watchCollection('names', function(newNames, oldNames) {
-            $scope.dataCount = newNames.length;
-          });
-
-          expect($scope.dataCount).toEqual(4);
-          $scope.$digest();
-
-          //still at 4 ... no changes
-          expect($scope.dataCount).toEqual(4);
-
-          $scope.names.pop();
-          $scope.$digest();
-
-          //now there's been a change
-          expect($scope.dataCount).toEqual(3);
-       * 
- * - * - * @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The expression value - * should evaluate to an object or an array which is observed on each - * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the collection will trigger - * a call to the `listener`. - * - * @param {function(newCollection, oldCollection, scope)} listener a callback function that is fired with both - * the `newCollection` and `oldCollection` as parameters. - * The `newCollection` object is the newly modified data obtained from the `obj` expression and the - * `oldCollection` object is a copy of the former collection data. - * The `scope` refers to the current scope. - * - * @returns {function()} Returns a de-registration function for this listener. When the de-registration function is executed - * then the internal watch operation is terminated. - */ - $watchCollection: function(obj, listener) { - var self = this; - var oldValue; - var newValue; - var changeDetected = 0; - var objGetter = $parse(obj); - var internalArray = []; - var internalObject = {}; - var oldLength = 0; - - function $watchCollectionWatch() { - newValue = objGetter(self); - var newLength, key; - - if (!isObject(newValue)) { - if (oldValue !== newValue) { - oldValue = newValue; - changeDetected++; - } - } else if (isArrayLike(newValue)) { - if (oldValue !== internalArray) { - // we are transitioning from something which was not an array into array. - oldValue = internalArray; - oldLength = oldValue.length = 0; - changeDetected++; - } - - newLength = newValue.length; - - if (oldLength !== newLength) { - // if lengths do not match we need to trigger change notification - changeDetected++; - oldValue.length = oldLength = newLength; - } - // copy the items to oldValue and look for changes. - for (var i = 0; i < newLength; i++) { - if (oldValue[i] !== newValue[i]) { - changeDetected++; - oldValue[i] = newValue[i]; - } - } - } else { - if (oldValue !== internalObject) { - // we are transitioning from something which was not an object into object. - oldValue = internalObject = {}; - oldLength = 0; - changeDetected++; - } - // copy the items to oldValue and look for changes. - newLength = 0; - for (key in newValue) { - if (newValue.hasOwnProperty(key)) { - newLength++; - if (oldValue.hasOwnProperty(key)) { - if (oldValue[key] !== newValue[key]) { - changeDetected++; - oldValue[key] = newValue[key]; - } - } else { - oldLength++; - oldValue[key] = newValue[key]; - changeDetected++; - } - } - } - if (oldLength > newLength) { - // we used to have more keys, need to find them and destroy them. - changeDetected++; - for(key in oldValue) { - if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) { - oldLength--; - delete oldValue[key]; - } - } - } - } - return changeDetected; - } - - function $watchCollectionAction() { - listener(newValue, oldValue, self); - } - - return this.$watch($watchCollectionWatch, $watchCollectionAction); - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$digest - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children. - * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the - * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are - * firing. This means that it is possible to get into an infinite loop. This function will throw - * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 10. - * - * Usually you don't call `$digest()` directly in - * {@link ng.directive:ngController controllers} or in - * {@link ng.$compileProvider#directive directives}. - * Instead a call to {@link ng.$rootScope.Scope#$apply $apply()} (typically from within a - * {@link ng.$compileProvider#directive directives}) will force a `$digest()`. - * - * If you want to be notified whenever `$digest()` is called, - * you can register a `watchExpression` function with {@link ng.$rootScope.Scope#$watch $watch()} - * with no `listener`. - * - * You may have a need to call `$digest()` from within unit-tests, to simulate the scope - * life-cycle. - * - * # Example - *
-           var scope = ...;
-           scope.name = 'misko';
-           scope.counter = 0;
-
-           expect(scope.counter).toEqual(0);
-           scope.$watch('name', function(newValue, oldValue) {
-             scope.counter = scope.counter + 1;
-           });
-           expect(scope.counter).toEqual(0);
-
-           scope.$digest();
-           // no variable change
-           expect(scope.counter).toEqual(0);
-
-           scope.name = 'adam';
-           scope.$digest();
-           expect(scope.counter).toEqual(1);
-       * 
- * - */ - $digest: function() { - var watch, value, last, - watchers, - asyncQueue = this.$$asyncQueue, - length, - dirty, ttl = TTL, - next, current, target = this, - watchLog = [], - logIdx, logMsg; - - beginPhase('$digest'); - - do { // "while dirty" loop - dirty = false; - current = target; - - while(asyncQueue.length) { - try { - current.$eval(asyncQueue.shift()); - } catch (e) { - $exceptionHandler(e); - } - } - - do { // "traverse the scopes" loop - if ((watchers = current.$$watchers)) { - // process our watches - length = watchers.length; - while (length--) { - try { - watch = watchers[length]; - // Most common watches are on primitives, in which case we can short - // circuit it with === operator, only when === fails do we use .equals - if ((value = watch.get(current)) !== (last = watch.last) && - !(watch.eq - ? equals(value, last) - : (typeof value == 'number' && typeof last == 'number' - && isNaN(value) && isNaN(last)))) { - dirty = true; - watch.last = watch.eq ? copy(value) : value; - watch.fn(value, ((last === initWatchVal) ? value : last), current); - if (ttl < 5) { - logIdx = 4 - ttl; - if (!watchLog[logIdx]) watchLog[logIdx] = []; - logMsg = (isFunction(watch.exp)) - ? 'fn: ' + (watch.exp.name || watch.exp.toString()) - : watch.exp; - logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last); - watchLog[logIdx].push(logMsg); - } - } - } catch (e) { - $exceptionHandler(e); - } - } - } - - // Insanity Warning: scope depth-first traversal - // yes, this code is a bit crazy, but it works and we have tests to prove it! - // this piece should be kept in sync with the traversal in $broadcast - if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { - while(current !== target && !(next = current.$$nextSibling)) { - current = current.$parent; - } - } - } while ((current = next)); - - if(dirty && !(ttl--)) { - clearPhase(); - throw Error(TTL + ' $digest() iterations reached. Aborting!\n' + - 'Watchers fired in the last 5 iterations: ' + toJson(watchLog)); - } - } while (dirty || asyncQueue.length); - - clearPhase(); - }, - - - /** - * @ngdoc event - * @name ng.$rootScope.Scope#$destroy - * @eventOf ng.$rootScope.Scope - * @eventType broadcast on scope being destroyed - * - * @description - * Broadcasted when a scope and its children are being destroyed. - */ - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$destroy - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Removes the current scope (and all of its children) from the parent scope. Removal implies - * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer - * propagate to the current scope and its children. Removal also implies that the current - * scope is eligible for garbage collection. - * - * The `$destroy()` is usually used by directives such as - * {@link ng.directive:ngRepeat ngRepeat} for managing the - * unrolling of the loop. - * - * Just before a scope is destroyed a `$destroy` event is broadcasted on this scope. - * Application code can register a `$destroy` event handler that will give it chance to - * perform any necessary cleanup. - */ - $destroy: function() { - // we can't destroy the root scope or a scope that has been already destroyed - if ($rootScope == this || this.$$destroyed) return; - var parent = this.$parent; - - this.$broadcast('$destroy'); - this.$$destroyed = true; - - if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; - if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; - if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; - if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; - - // This is bogus code that works around Chrome's GC leak - // see: https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 - this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = - this.$$childTail = null; - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$eval - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Executes the `expression` on the current scope returning the result. Any exceptions in the - * expression are propagated (uncaught). This is useful when evaluating Angular expressions. - * - * # Example - *
-           var scope = ng.$rootScope.Scope();
-           scope.a = 1;
-           scope.b = 2;
-
-           expect(scope.$eval('a+b')).toEqual(3);
-           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
-       * 
- * - * @param {(string|function())=} expression An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with the current `scope` parameter. - * - * @returns {*} The result of evaluating the expression. - */ - $eval: function(expr, locals) { - return $parse(expr)(this, locals); - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$evalAsync - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Executes the expression on the current scope at a later point in time. - * - * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that: - * - * - it will execute in the current script execution context (before any DOM rendering). - * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after - * `expression` execution. - * - * Any exceptions from the execution of the expression are forwarded to the - * {@link ng.$exceptionHandler $exceptionHandler} service. - * - * @param {(string|function())=} expression An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with the current `scope` parameter. - * - */ - $evalAsync: function(expr) { - this.$$asyncQueue.push(expr); - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$apply - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * `$apply()` is used to execute an expression in angular from outside of the angular framework. - * (For example from browser DOM events, setTimeout, XHR or third party libraries). - * Because we are calling into the angular framework we need to perform proper scope life-cycle - * of {@link ng.$exceptionHandler exception handling}, - * {@link ng.$rootScope.Scope#$digest executing watches}. - * - * ## Life cycle - * - * # Pseudo-Code of `$apply()` - *
-           function $apply(expr) {
-             try {
-               return $eval(expr);
-             } catch (e) {
-               $exceptionHandler(e);
-             } finally {
-               $root.$digest();
-             }
-           }
-       * 
- * - * - * Scope's `$apply()` method transitions through the following stages: - * - * 1. The {@link guide/expression expression} is executed using the - * {@link ng.$rootScope.Scope#$eval $eval()} method. - * 2. Any exceptions from the execution of the expression are forwarded to the - * {@link ng.$exceptionHandler $exceptionHandler} service. - * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the expression - * was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. - * - * - * @param {(string|function())=} exp An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with current `scope` parameter. - * - * @returns {*} The result of evaluating the expression. - */ - $apply: function(expr) { - try { - beginPhase('$apply'); - return this.$eval(expr); - } catch (e) { - $exceptionHandler(e); - } finally { - clearPhase(); - try { - $rootScope.$digest(); - } catch (e) { - $exceptionHandler(e); - throw e; - } - } - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$on - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of - * event life cycle. - * - * The event listener function format is: `function(event, args...)`. The `event` object - * passed into the listener has the following attributes: - * - * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or `$broadcast`-ed. - * - `currentScope` - `{Scope}`: the current scope which is handling the event. - * - `name` - `{string}`: Name of the event. - * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel further event - * propagation (available only for events that were `$emit`-ed). - * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true. - * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. - * - * @param {string} name Event name to listen on. - * @param {function(event, args...)} listener Function to call when the event is emitted. - * @returns {function()} Returns a deregistration function for this listener. - */ - $on: function(name, listener) { - var namedListeners = this.$$listeners[name]; - if (!namedListeners) { - this.$$listeners[name] = namedListeners = []; - } - namedListeners.push(listener); - - return function() { - namedListeners[indexOf(namedListeners, listener)] = null; - }; - }, - - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$emit - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Dispatches an event `name` upwards through the scope hierarchy notifying the - * registered {@link ng.$rootScope.Scope#$on} listeners. - * - * The event life cycle starts at the scope on which `$emit` was called. All - * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified. - * Afterwards, the event traverses upwards toward the root scope and calls all registered - * listeners along the way. The event will stop propagating if one of the listeners cancels it. - * - * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed - * onto the {@link ng.$exceptionHandler $exceptionHandler} service. - * - * @param {string} name Event name to emit. - * @param {...*} args Optional set of arguments which will be passed onto the event listeners. - * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} - */ - $emit: function(name, args) { - var empty = [], - namedListeners, - scope = this, - stopPropagation = false, - event = { - name: name, - targetScope: scope, - stopPropagation: function() {stopPropagation = true;}, - preventDefault: function() { - event.defaultPrevented = true; - }, - defaultPrevented: false - }, - listenerArgs = concat([event], arguments, 1), - i, length; - - do { - namedListeners = scope.$$listeners[name] || empty; - event.currentScope = scope; - for (i=0, length=namedListeners.length; i 7), - hasEvent: function(event) { - // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have - // it. In particular the event is not fired when backspace or delete key are pressed or - // when cut operation is performed. - if (event == 'input' && msie == 9) return false; - - if (isUndefined(eventSupport[event])) { - var divElm = document.createElement('div'); - eventSupport[event] = 'on' + event in divElm; - } - - return eventSupport[event]; - }, - csp: document.securityPolicy ? document.securityPolicy.isActive : false, - vendorPrefix: vendorPrefix, - transitions : transitions, - animations : animations - }; - }]; -} - -/** - * @ngdoc object - * @name ng.$window - * - * @description - * A reference to the browser's `window` object. While `window` - * is globally available in JavaScript, it causes testability problems, because - * it is a global variable. In angular we always refer to it through the - * `$window` service, so it may be overridden, removed or mocked for testing. - * - * All expressions are evaluated with respect to current scope so they don't - * suffer from window globality. - * - * @example - - - -
- - -
-
- - it('should display the greeting in the input box', function() { - input('greeting').enter('Hello, E2E Tests'); - // If we click the button it will block the test runner - // element(':button').click(); - }); - -
- */ -function $WindowProvider(){ - this.$get = valueFn(window); -} - -/** - * Parse headers into key value object - * - * @param {string} headers Raw headers as a string - * @returns {Object} Parsed headers as key value object - */ -function parseHeaders(headers) { - var parsed = {}, key, val, i; - - if (!headers) return parsed; - - forEach(headers.split('\n'), function(line) { - i = line.indexOf(':'); - key = lowercase(trim(line.substr(0, i))); - val = trim(line.substr(i + 1)); - - if (key) { - if (parsed[key]) { - parsed[key] += ', ' + val; - } else { - parsed[key] = val; - } - } - }); - - return parsed; -} - - -var IS_SAME_DOMAIN_URL_MATCH = /^(([^:]+):)?\/\/(\w+:{0,1}\w*@)?([\w\.-]*)?(:([0-9]+))?(.*)$/; - - -/** - * Parse a request and location URL and determine whether this is a same-domain request. - * - * @param {string} requestUrl The url of the request. - * @param {string} locationUrl The current browser location url. - * @returns {boolean} Whether the request is for the same domain. - */ -function isSameDomain(requestUrl, locationUrl) { - var match = IS_SAME_DOMAIN_URL_MATCH.exec(requestUrl); - // if requestUrl is relative, the regex does not match. - if (match == null) return true; - - var domain1 = { - protocol: match[2], - host: match[4], - port: int(match[6]) || DEFAULT_PORTS[match[2]] || null, - // IE8 sets unmatched groups to '' instead of undefined. - relativeProtocol: match[2] === undefined || match[2] === '' - }; - - match = SERVER_MATCH.exec(locationUrl); - var domain2 = { - protocol: match[1], - host: match[3], - port: int(match[5]) || DEFAULT_PORTS[match[1]] || null - }; - - return (domain1.protocol == domain2.protocol || domain1.relativeProtocol) && - domain1.host == domain2.host && - (domain1.port == domain2.port || (domain1.relativeProtocol && - domain2.port == DEFAULT_PORTS[domain2.protocol])); -} - - -/** - * Returns a function that provides access to parsed headers. - * - * Headers are lazy parsed when first requested. - * @see parseHeaders - * - * @param {(string|Object)} headers Headers to provide access to. - * @returns {function(string=)} Returns a getter function which if called with: - * - * - if called with single an argument returns a single header value or null - * - if called with no arguments returns an object containing all headers. - */ -function headersGetter(headers) { - var headersObj = isObject(headers) ? headers : undefined; - - return function(name) { - if (!headersObj) headersObj = parseHeaders(headers); - - if (name) { - return headersObj[lowercase(name)] || null; - } - - return headersObj; - }; -} - - -/** - * Chain all given functions - * - * This function is used for both request and response transforming - * - * @param {*} data Data to transform. - * @param {function(string=)} headers Http headers getter fn. - * @param {(function|Array.)} fns Function or an array of functions. - * @returns {*} Transformed data. - */ -function transformData(data, headers, fns) { - if (isFunction(fns)) - return fns(data, headers); - - forEach(fns, function(fn) { - data = fn(data, headers); - }); - - return data; -} - - -function isSuccess(status) { - return 200 <= status && status < 300; -} - - -function $HttpProvider() { - var JSON_START = /^\s*(\[|\{[^\{])/, - JSON_END = /[\}\]]\s*$/, - PROTECTION_PREFIX = /^\)\]\}',?\n/, - CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'}; - - var defaults = this.defaults = { - // transform incoming response data - transformResponse: [function(data) { - if (isString(data)) { - // strip json vulnerability protection prefix - data = data.replace(PROTECTION_PREFIX, ''); - if (JSON_START.test(data) && JSON_END.test(data)) - data = fromJson(data, true); - } - return data; - }], - - // transform outgoing request data - transformRequest: [function(d) { - return isObject(d) && !isFile(d) ? toJson(d) : d; - }], - - // default headers - headers: { - common: { - 'Accept': 'application/json, text/plain, */*' - }, - post: CONTENT_TYPE_APPLICATION_JSON, - put: CONTENT_TYPE_APPLICATION_JSON, - patch: CONTENT_TYPE_APPLICATION_JSON - }, - - xsrfCookieName: 'XSRF-TOKEN', - xsrfHeaderName: 'X-XSRF-TOKEN' - }; - - /** - * Are order by request. I.E. they are applied in the same order as - * array on request, but revers order on response. - */ - var interceptorFactories = this.interceptors = []; - /** - * For historical reasons, response interceptors ordered by the order in which - * they are applied to response. (This is in revers to interceptorFactories) - */ - var responseInterceptorFactories = this.responseInterceptors = []; - - this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', - function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) { - - var defaultCache = $cacheFactory('$http'); - - /** - * Interceptors stored in reverse order. Inner interceptors before outer interceptors. - * The reversal is needed so that we can build up the interception chain around the - * server request. - */ - var reversedInterceptors = []; - - forEach(interceptorFactories, function(interceptorFactory) { - reversedInterceptors.unshift(isString(interceptorFactory) - ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); - }); - - forEach(responseInterceptorFactories, function(interceptorFactory, index) { - var responseFn = isString(interceptorFactory) - ? $injector.get(interceptorFactory) - : $injector.invoke(interceptorFactory); - - /** - * Response interceptors go before "around" interceptors (no real reason, just - * had to pick one.) But they are already revesed, so we can't use unshift, hence - * the splice. - */ - reversedInterceptors.splice(index, 0, { - response: function(response) { - return responseFn($q.when(response)); - }, - responseError: function(response) { - return responseFn($q.reject(response)); - } - }); - }); - - - /** - * @ngdoc function - * @name ng.$http - * @requires $httpBackend - * @requires $browser - * @requires $cacheFactory - * @requires $rootScope - * @requires $q - * @requires $injector - * - * @description - * The `$http` service is a core Angular service that facilitates communication with the remote - * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest - * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}. - * - * For unit testing applications that use `$http` service, see - * {@link ngMock.$httpBackend $httpBackend mock}. - * - * For a higher level of abstraction, please check out the {@link ngResource.$resource - * $resource} service. - * - * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by - * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage - * it is important to familiarize yourself with these APIs and the guarantees they provide. - * - * - * # General usage - * The `$http` service is a function which takes a single argument — a configuration object — - * that is used to generate an HTTP request and returns a {@link ng.$q promise} - * with two $http specific methods: `success` and `error`. - * - *
-     *   $http({method: 'GET', url: '/someUrl'}).
-     *     success(function(data, status, headers, config) {
-     *       // this callback will be called asynchronously
-     *       // when the response is available
-     *     }).
-     *     error(function(data, status, headers, config) {
-     *       // called asynchronously if an error occurs
-     *       // or server returns response with an error status.
-     *     });
-     * 
- * - * Since the returned value of calling the $http function is a `promise`, you can also use - * the `then` method to register callbacks, and these callbacks will receive a single argument – - * an object representing the response. See the API signature and type info below for more - * details. - * - * A response status code between 200 and 299 is considered a success status and - * will result in the success callback being called. Note that if the response is a redirect, - * XMLHttpRequest will transparently follow it, meaning that the error callback will not be - * called for such responses. - * - * # Shortcut methods - * - * Since all invocations of the $http service require passing in an HTTP method and URL, and - * POST/PUT requests require request data to be provided as well, shortcut methods - * were created: - * - *
-     *   $http.get('/someUrl').success(successCallback);
-     *   $http.post('/someUrl', data).success(successCallback);
-     * 
- * - * Complete list of shortcut methods: - * - * - {@link ng.$http#get $http.get} - * - {@link ng.$http#head $http.head} - * - {@link ng.$http#post $http.post} - * - {@link ng.$http#put $http.put} - * - {@link ng.$http#delete $http.delete} - * - {@link ng.$http#jsonp $http.jsonp} - * - * - * # Setting HTTP Headers - * - * The $http service will automatically add certain HTTP headers to all requests. These defaults - * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration - * object, which currently contains this default configuration: - * - * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): - * - `Accept: application/json, text/plain, * / *` - * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) - * - `Content-Type: application/json` - * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) - * - `Content-Type: application/json` - * - * To add or overwrite these defaults, simply add or remove a property from these configuration - * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object - * with the lowercased HTTP method name as the key, e.g. - * `$httpProvider.defaults.headers.get['My-Header']='value'`. - * - * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same - * fashion. - * - * - * # Transforming Requests and Responses - * - * Both requests and responses can be transformed using transform functions. By default, Angular - * applies these transformations: - * - * Request transformations: - * - * - If the `data` property of the request configuration object contains an object, serialize it into - * JSON format. - * - * Response transformations: - * - * - If XSRF prefix is detected, strip it (see Security Considerations section below). - * - If JSON response is detected, deserialize it using a JSON parser. - * - * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and - * `$httpProvider.defaults.transformResponse` properties. These properties are by default an - * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the - * transformation chain. You can also decide to completely override any default transformations by assigning your - * transformation functions to these properties directly without the array wrapper. - * - * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or - * `transformResponse` properties of the configuration object passed into `$http`. - * - * - * # Caching - * - * To enable caching, set the configuration property `cache` to `true`. When the cache is - * enabled, `$http` stores the response from the server in local cache. Next time the - * response is served from the cache without sending a request to the server. - * - * Note that even if the response is served from cache, delivery of the data is asynchronous in - * the same way that real requests are. - * - * If there are multiple GET requests for the same URL that should be cached using the same - * cache, but the cache is not populated yet, only one request to the server will be made and - * the remaining requests will be fulfilled using the response from the first request. - * - * A custom default cache built with $cacheFactory can be provided in $http.defaults.cache. - * To skip it, set configuration property `cache` to `false`. - * - * - * # Interceptors - * - * Before you start creating interceptors, be sure to understand the - * {@link ng.$q $q and deferred/promise APIs}. - * - * For purposes of global error handling, authentication, or any kind of synchronous or - * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be - * able to intercept requests before they are handed to the server and - * responses before they are handed over to the application code that - * initiated these requests. The interceptors leverage the {@link ng.$q - * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. - * - * The interceptors are service factories that are registered with the `$httpProvider` by - * adding them to the `$httpProvider.interceptors` array. The factory is called and - * injected with dependencies (if specified) and returns the interceptor. - * - * There are two kinds of interceptors (and two kinds of rejection interceptors): - * - * * `request`: interceptors get called with http `config` object. The function is free to modify - * the `config` or create a new one. The function needs to return the `config` directly or as a - * promise. - * * `requestError`: interceptor gets called when a previous interceptor threw an error or resolved - * with a rejection. - * * `response`: interceptors get called with http `response` object. The function is free to modify - * the `response` or create a new one. The function needs to return the `response` directly or as a - * promise. - * * `responseError`: interceptor gets called when a previous interceptor threw an error or resolved - * with a rejection. - * - * - *
-     *   // register the interceptor as a service
-     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
-     *     return {
-     *       // optional method
-     *       'request': function(config) {
-     *         // do something on success
-     *         return config || $q.when(config);
-     *       },
-     *
-     *       // optional method
-     *      'requestError': function(rejection) {
-     *         // do something on error
-     *         if (canRecover(rejection)) {
-     *           return responseOrNewPromise
-     *         }
-     *         return $q.reject(rejection);
-     *       },
-     *
-     *
-     *
-     *       // optional method
-     *       'response': function(response) {
-     *         // do something on success
-     *         return response || $q.when(response);
-     *       },
-     *
-     *       // optional method
-     *      'responseError': function(rejection) {
-     *         // do something on error
-     *         if (canRecover(rejection)) {
-     *           return responseOrNewPromise
-     *         }
-     *         return $q.reject(rejection);
-     *       };
-     *     }
-     *   });
-     *
-     *   $httpProvider.interceptors.push('myHttpInterceptor');
-     *
-     *
-     *   // register the interceptor via an anonymous factory
-     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
-     *     return {
-     *      'request': function(config) {
-     *          // same as above
-     *       },
-     *       'response': function(response) {
-     *          // same as above
-     *       }
-     *   });
-     * 
- * - * # Response interceptors (DEPRECATED) - * - * Before you start creating interceptors, be sure to understand the - * {@link ng.$q $q and deferred/promise APIs}. - * - * For purposes of global error handling, authentication or any kind of synchronous or - * asynchronous preprocessing of received responses, it is desirable to be able to intercept - * responses for http requests before they are handed over to the application code that - * initiated these requests. The response interceptors leverage the {@link ng.$q - * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing. - * - * The interceptors are service factories that are registered with the $httpProvider by - * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and - * injected with dependencies (if specified) and returns the interceptor — a function that - * takes a {@link ng.$q promise} and returns the original or a new promise. - * - *
-     *   // register the interceptor as a service
-     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
-     *     return function(promise) {
-     *       return promise.then(function(response) {
-     *         // do something on success
-     *       }, function(response) {
-     *         // do something on error
-     *         if (canRecover(response)) {
-     *           return responseOrNewPromise
-     *         }
-     *         return $q.reject(response);
-     *       });
-     *     }
-     *   });
-     *
-     *   $httpProvider.responseInterceptors.push('myHttpInterceptor');
-     *
-     *
-     *   // register the interceptor via an anonymous factory
-     *   $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) {
-     *     return function(promise) {
-     *       // same as above
-     *     }
-     *   });
-     * 
- * - * - * # Security Considerations - * - * When designing web applications, consider security threats from: - * - * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx - * JSON vulnerability} - * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} - * - * Both server and the client must cooperate in order to eliminate these threats. Angular comes - * pre-configured with strategies that address these issues, but for this to work backend server - * cooperation is required. - * - * ## JSON Vulnerability Protection - * - * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx - * JSON vulnerability} allows third party website to turn your JSON resource URL into - * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To - * counter this your server can prefix all JSON requests with following string `")]}',\n"`. - * Angular will automatically strip the prefix before processing it as JSON. - * - * For example if your server needs to return: - *
-     * ['one','two']
-     * 
- * - * which is vulnerable to attack, your server can return: - *
-     * )]}',
-     * ['one','two']
-     * 
- * - * Angular will strip the prefix, before processing the JSON. - * - * - * ## Cross Site Request Forgery (XSRF) Protection - * - * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which - * an unauthorized site can gain your user's private data. Angular provides a mechanism - * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie - * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only - * JavaScript that runs on your domain could read the cookie, your server can be assured that - * the XHR came from JavaScript running on your domain. The header will not be set for - * cross-domain requests. - * - * To take advantage of this, your server needs to set a token in a JavaScript readable session - * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the - * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure - * that only JavaScript running on your domain could have sent the request. The token must be - * unique for each user and must be verifiable by the server (to prevent the JavaScript from making - * up its own tokens). We recommend that the token is a digest of your site's authentication - * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security. - * - * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName - * properties of either $httpProvider.defaults, or the per-request config object. - * - * - * @param {object} config Object describing the request to be made and how it should be - * processed. The object has following properties: - * - * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) - * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. - * - **params** – `{Object.}` – Map of strings or objects which will be turned to - * `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified. - * - **data** – `{string|Object}` – Data to be sent as the request message data. - * - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server. - * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. - * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. - * - **transformRequest** – `{function(data, headersGetter)|Array.}` – - * transform function or an array of such functions. The transform function takes the http - * request body and headers and returns its transformed (typically serialized) version. - * - **transformResponse** – `{function(data, headersGetter)|Array.}` – - * transform function or an array of such functions. The transform function takes the http - * response body and headers and returns its transformed (typically deserialized) version. - * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the - * GET request, otherwise if a cache instance built with - * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for - * caching. - * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} - * that should abort the request when resolved. - * - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the - * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5 - * requests with credentials} for more information. - * - **responseType** - `{string}` - see {@link - * https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}. - * - * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the - * standard `then` method and two http specific methods: `success` and `error`. The `then` - * method takes two arguments a success and an error callback which will be called with a - * response object. The `success` and `error` methods take a single argument - a function that - * will be called when the request succeeds or fails respectively. The arguments passed into - * these functions are destructured representation of the response object passed into the - * `then` method. The response object has these properties: - * - * - **data** – `{string|Object}` – The response body transformed with the transform functions. - * - **status** – `{number}` – HTTP status code of the response. - * - **headers** – `{function([headerName])}` – Header getter function. - * - **config** – `{Object}` – The configuration object that was used to generate the request. - * - * @property {Array.} pendingRequests Array of config objects for currently pending - * requests. This is primarily meant to be used for debugging purposes. - * - * - * @example - - -
- - -
- - - -
http status code: {{status}}
-
http response data: {{data}}
-
-
- - function FetchCtrl($scope, $http, $templateCache) { - $scope.method = 'GET'; - $scope.url = 'http-hello.html'; - - $scope.fetch = function() { - $scope.code = null; - $scope.response = null; - - $http({method: $scope.method, url: $scope.url, cache: $templateCache}). - success(function(data, status) { - $scope.status = status; - $scope.data = data; - }). - error(function(data, status) { - $scope.data = data || "Request failed"; - $scope.status = status; - }); - }; - - $scope.updateModel = function(method, url) { - $scope.method = method; - $scope.url = url; - }; - } - - - Hello, $http! - - - it('should make an xhr GET request', function() { - element(':button:contains("Sample GET")').click(); - element(':button:contains("fetch")').click(); - expect(binding('status')).toBe('200'); - expect(binding('data')).toMatch(/Hello, \$http!/); - }); - - it('should make a JSONP request to angularjs.org', function() { - element(':button:contains("Sample JSONP")').click(); - element(':button:contains("fetch")').click(); - expect(binding('status')).toBe('200'); - expect(binding('data')).toMatch(/Super Hero!/); - }); - - it('should make JSONP request to invalid URL and invoke the error handler', - function() { - element(':button:contains("Invalid JSONP")').click(); - element(':button:contains("fetch")').click(); - expect(binding('status')).toBe('0'); - expect(binding('data')).toBe('Request failed'); - }); - -
- */ - function $http(requestConfig) { - var config = { - transformRequest: defaults.transformRequest, - transformResponse: defaults.transformResponse - }; - var headers = {}; - - extend(config, requestConfig); - config.headers = headers; - config.method = uppercase(config.method); - - extend(headers, - defaults.headers.common, - defaults.headers[lowercase(config.method)], - requestConfig.headers); - - var xsrfValue = isSameDomain(config.url, $browser.url()) - ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName] - : undefined; - if (xsrfValue) { - headers[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; - } - - - var serverRequest = function(config) { - var reqData = transformData(config.data, headersGetter(headers), config.transformRequest); - - // strip content-type if data is undefined - if (isUndefined(config.data)) { - delete headers['Content-Type']; - } - - if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { - config.withCredentials = defaults.withCredentials; - } - - // send request - return sendReq(config, reqData, headers).then(transformResponse, transformResponse); - }; - - var chain = [serverRequest, undefined]; - var promise = $q.when(config); - - // apply interceptors - forEach(reversedInterceptors, function(interceptor) { - if (interceptor.request || interceptor.requestError) { - chain.unshift(interceptor.request, interceptor.requestError); - } - if (interceptor.response || interceptor.responseError) { - chain.push(interceptor.response, interceptor.responseError); - } - }); - - while(chain.length) { - var thenFn = chain.shift(); - var rejectFn = chain.shift(); - - promise = promise.then(thenFn, rejectFn); - } - - promise.success = function(fn) { - promise.then(function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; - - promise.error = function(fn) { - promise.then(null, function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; - - return promise; - - function transformResponse(response) { - // make a copy since the response must be cacheable - var resp = extend({}, response, { - data: transformData(response.data, response.headers, config.transformResponse) - }); - return (isSuccess(response.status)) - ? resp - : $q.reject(resp); - } - } - - $http.pendingRequests = []; - - /** - * @ngdoc method - * @name ng.$http#get - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `GET` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name ng.$http#delete - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `DELETE` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name ng.$http#head - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `HEAD` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name ng.$http#jsonp - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `JSONP` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request. - * Should contain `JSON_CALLBACK` string. - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - createShortMethods('get', 'delete', 'head', 'jsonp'); - - /** - * @ngdoc method - * @name ng.$http#post - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `POST` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name ng.$http#put - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `PUT` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - createShortMethodsWithData('post', 'put'); - - /** - * @ngdoc property - * @name ng.$http#defaults - * @propertyOf ng.$http - * - * @description - * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of - * default headers, withCredentials as well as request and response transformations. - * - * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. - */ - $http.defaults = defaults; - - - return $http; - - - function createShortMethods(names) { - forEach(arguments, function(name) { - $http[name] = function(url, config) { - return $http(extend(config || {}, { - method: name, - url: url - })); - }; - }); - } - - - function createShortMethodsWithData(name) { - forEach(arguments, function(name) { - $http[name] = function(url, data, config) { - return $http(extend(config || {}, { - method: name, - url: url, - data: data - })); - }; - }); - } - - - /** - * Makes the request. - * - * !!! ACCESSES CLOSURE VARS: - * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests - */ - function sendReq(config, reqData, reqHeaders) { - var deferred = $q.defer(), - promise = deferred.promise, - cache, - cachedResp, - url = buildUrl(config.url, config.params); - - $http.pendingRequests.push(config); - promise.then(removePendingReq, removePendingReq); - - - if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') { - cache = isObject(config.cache) ? config.cache - : isObject(defaults.cache) ? defaults.cache - : defaultCache; - } - - if (cache) { - cachedResp = cache.get(url); - if (cachedResp) { - if (cachedResp.then) { - // cached request has already been sent, but there is no response yet - cachedResp.then(removePendingReq, removePendingReq); - return cachedResp; - } else { - // serving from cache - if (isArray(cachedResp)) { - resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2])); - } else { - resolvePromise(cachedResp, 200, {}); - } - } - } else { - // put the promise for the non-transformed response into cache as a placeholder - cache.put(url, promise); - } - } - - // if we won't have the response in cache, send the request to the backend - if (!cachedResp) { - $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, - config.withCredentials, config.responseType); - } - - return promise; - - - /** - * Callback registered to $httpBackend(): - * - caches the response if desired - * - resolves the raw $http promise - * - calls $apply - */ - function done(status, response, headersString) { - if (cache) { - if (isSuccess(status)) { - cache.put(url, [status, response, parseHeaders(headersString)]); - } else { - // remove promise from the cache - cache.remove(url); - } - } - - resolvePromise(response, status, headersString); - if (!$rootScope.$$phase) $rootScope.$apply(); - } - - - /** - * Resolves the raw $http promise. - */ - function resolvePromise(response, status, headers) { - // normalize internal statuses to 0 - status = Math.max(status, 0); - - (isSuccess(status) ? deferred.resolve : deferred.reject)({ - data: response, - status: status, - headers: headersGetter(headers), - config: config - }); - } - - - function removePendingReq() { - var idx = indexOf($http.pendingRequests, config); - if (idx !== -1) $http.pendingRequests.splice(idx, 1); - } - } - - - function buildUrl(url, params) { - if (!params) return url; - var parts = []; - forEachSorted(params, function(value, key) { - if (value == null || value == undefined) return; - if (!isArray(value)) value = [value]; - - forEach(value, function(v) { - if (isObject(v)) { - v = toJson(v); - } - parts.push(encodeUriQuery(key) + '=' + - encodeUriQuery(v)); - }); - }); - return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); - } - - - }]; -} - -var XHR = window.XMLHttpRequest || function() { - try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} - try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {} - try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {} - throw new Error("This browser does not support XMLHttpRequest."); -}; - - -/** - * @ngdoc object - * @name ng.$httpBackend - * @requires $browser - * @requires $window - * @requires $document - * - * @description - * HTTP backend used by the {@link ng.$http service} that delegates to - * XMLHttpRequest object or JSONP and deals with browser incompatibilities. - * - * You should never need to use this service directly, instead use the higher-level abstractions: - * {@link ng.$http $http} or {@link ngResource.$resource $resource}. - * - * During testing this implementation is swapped with {@link ngMock.$httpBackend mock - * $httpBackend} which can be trained with responses. - */ -function $HttpBackendProvider() { - this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { - return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks, - $document[0], $window.location.protocol.replace(':', '')); - }]; -} - -function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) { - // TODO(vojta): fix the signature - return function(method, url, post, callback, headers, timeout, withCredentials, responseType) { - var status; - $browser.$$incOutstandingRequestCount(); - url = url || $browser.url(); - - if (lowercase(method) == 'jsonp') { - var callbackId = '_' + (callbacks.counter++).toString(36); - callbacks[callbackId] = function(data) { - callbacks[callbackId].data = data; - }; - - var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), - function() { - if (callbacks[callbackId].data) { - completeRequest(callback, 200, callbacks[callbackId].data); - } else { - completeRequest(callback, status || -2); - } - delete callbacks[callbackId]; - }); - } else { - var xhr = new XHR(); - xhr.open(method, url, true); - forEach(headers, function(value, key) { - if (value) xhr.setRequestHeader(key, value); - }); - - // In IE6 and 7, this might be called synchronously when xhr.send below is called and the - // response is in the cache. the promise api will ensure that to the app code the api is - // always async - xhr.onreadystatechange = function() { - if (xhr.readyState == 4) { - var responseHeaders = xhr.getAllResponseHeaders(); - - // TODO(vojta): remove once Firefox 21 gets released. - // begin: workaround to overcome Firefox CORS http response headers bug - // https://bugzilla.mozilla.org/show_bug.cgi?id=608735 - // Firefox already patched in nightly. Should land in Firefox 21. - - // CORS "simple response headers" http://www.w3.org/TR/cors/ - var value, - simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type", - "Expires", "Last-Modified", "Pragma"]; - if (!responseHeaders) { - responseHeaders = ""; - forEach(simpleHeaders, function (header) { - var value = xhr.getResponseHeader(header); - if (value) { - responseHeaders += header + ": " + value + "\n"; - } - }); - } - // end of the workaround. - - // responseText is the old-school way of retrieving response (supported by IE8 & 9) - // response and responseType properties were introduced in XHR Level2 spec (supported by IE10) - completeRequest(callback, - status || xhr.status, - (xhr.responseType ? xhr.response : xhr.responseText), - responseHeaders); - } - }; - - if (withCredentials) { - xhr.withCredentials = true; - } - - if (responseType) { - xhr.responseType = responseType; - } - - xhr.send(post || ''); - } - - if (timeout > 0) { - var timeoutId = $browserDefer(timeoutRequest, timeout); - } else if (timeout && timeout.then) { - timeout.then(timeoutRequest); - } - - - function timeoutRequest() { - status = -1; - jsonpDone && jsonpDone(); - xhr && xhr.abort(); - } - - function completeRequest(callback, status, response, headersString) { - // URL_MATCH is defined in src/service/location.js - var protocol = (url.match(SERVER_MATCH) || ['', locationProtocol])[1]; - - // cancel timeout and subsequent timeout promise resolution - timeoutId && $browserDefer.cancel(timeoutId); - jsonpDone = xhr = null; - - // fix status code for file protocol (it's always 0) - status = (protocol == 'file') ? (response ? 200 : 404) : status; - - // normalize IE bug (http://bugs.jquery.com/ticket/1450) - status = status == 1223 ? 204 : status; - - callback(status, response, headersString); - $browser.$$completeOutstandingRequest(noop); - } - }; - - function jsonpReq(url, done) { - // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: - // - fetches local scripts via XHR and evals them - // - adds and immediately removes script elements from the document - var script = rawDocument.createElement('script'), - doneWrapper = function() { - rawDocument.body.removeChild(script); - if (done) done(); - }; - - script.type = 'text/javascript'; - script.src = url; - - if (msie) { - script.onreadystatechange = function() { - if (/loaded|complete/.test(script.readyState)) doneWrapper(); - }; - } else { - script.onload = script.onerror = doneWrapper; - } - - rawDocument.body.appendChild(script); - return doneWrapper; - } -} - -/** - * @ngdoc object - * @name ng.$locale - * - * @description - * $locale service provides localization rules for various Angular components. As of right now the - * only public api is: - * - * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) - */ -function $LocaleProvider(){ - this.$get = function() { - return { - id: 'en-us', - - NUMBER_FORMATS: { - DECIMAL_SEP: '.', - GROUP_SEP: ',', - PATTERNS: [ - { // Decimal Pattern - minInt: 1, - minFrac: 0, - maxFrac: 3, - posPre: '', - posSuf: '', - negPre: '-', - negSuf: '', - gSize: 3, - lgSize: 3 - },{ //Currency Pattern - minInt: 1, - minFrac: 2, - maxFrac: 2, - posPre: '\u00A4', - posSuf: '', - negPre: '(\u00A4', - negSuf: ')', - gSize: 3, - lgSize: 3 - } - ], - CURRENCY_SYM: '$' - }, - - DATETIME_FORMATS: { - MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December' - .split(','), - SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), - DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), - SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), - AMPMS: ['AM','PM'], - medium: 'MMM d, y h:mm:ss a', - short: 'M/d/yy h:mm a', - fullDate: 'EEEE, MMMM d, y', - longDate: 'MMMM d, y', - mediumDate: 'MMM d, y', - shortDate: 'M/d/yy', - mediumTime: 'h:mm:ss a', - shortTime: 'h:mm a' - }, - - pluralCat: function(num) { - if (num === 1) { - return 'one'; - } - return 'other'; - } - }; - }; -} - -function $TimeoutProvider() { - this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler', - function($rootScope, $browser, $q, $exceptionHandler) { - var deferreds = {}; - - - /** - * @ngdoc function - * @name ng.$timeout - * @requires $browser - * - * @description - * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch - * block and delegates any exceptions to - * {@link ng.$exceptionHandler $exceptionHandler} service. - * - * The return value of registering a timeout function is a promise, which will be resolved when - * the timeout is reached and the timeout function is executed. - * - * To cancel a timeout request, call `$timeout.cancel(promise)`. - * - * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to - * synchronously flush the queue of deferred functions. - * - * @param {function()} fn A function, whose execution should be delayed. - * @param {number=} [delay=0] Delay in milliseconds. - * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise - * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. - * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this - * promise will be resolved with is the return value of the `fn` function. - */ - function timeout(fn, delay, invokeApply) { - var deferred = $q.defer(), - promise = deferred.promise, - skipApply = (isDefined(invokeApply) && !invokeApply), - timeoutId, cleanup; - - timeoutId = $browser.defer(function() { - try { - deferred.resolve(fn()); - } catch(e) { - deferred.reject(e); - $exceptionHandler(e); - } - - if (!skipApply) $rootScope.$apply(); - }, delay); - - cleanup = function() { - delete deferreds[promise.$$timeoutId]; - }; - - promise.$$timeoutId = timeoutId; - deferreds[timeoutId] = deferred; - promise.then(cleanup, cleanup); - - return promise; - } - - - /** - * @ngdoc function - * @name ng.$timeout#cancel - * @methodOf ng.$timeout - * - * @description - * Cancels a task associated with the `promise`. As a result of this, the promise will be - * resolved with a rejection. - * - * @param {Promise=} promise Promise returned by the `$timeout` function. - * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully - * canceled. - */ - timeout.cancel = function(promise) { - if (promise && promise.$$timeoutId in deferreds) { - deferreds[promise.$$timeoutId].reject('canceled'); - return $browser.defer.cancel(promise.$$timeoutId); - } - return false; - }; - - return timeout; - }]; -} - -/** - * @ngdoc object - * @name ng.$filterProvider - * @description - * - * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To - * achieve this a filter definition consists of a factory function which is annotated with dependencies and is - * responsible for creating a filter function. - * - *
- *   // Filter registration
- *   function MyModule($provide, $filterProvider) {
- *     // create a service to demonstrate injection (not always needed)
- *     $provide.value('greet', function(name){
- *       return 'Hello ' + name + '!';
- *     });
- *
- *     // register a filter factory which uses the
- *     // greet service to demonstrate DI.
- *     $filterProvider.register('greet', function(greet){
- *       // return the filter function which uses the greet service
- *       // to generate salutation
- *       return function(text) {
- *         // filters need to be forgiving so check input validity
- *         return text && greet(text) || text;
- *       };
- *     });
- *   }
- * 
- * - * The filter function is registered with the `$injector` under the filter name suffixe with `Filter`. - *
- *   it('should be the same instance', inject(
- *     function($filterProvider) {
- *       $filterProvider.register('reverse', function(){
- *         return ...;
- *       });
- *     },
- *     function($filter, reverseFilter) {
- *       expect($filter('reverse')).toBe(reverseFilter);
- *     });
- * 
- * - * - * For more information about how angular filters work, and how to create your own filters, see - * {@link guide/dev_guide.templates.filters Understanding Angular Filters} in the angular Developer - * Guide. - */ -/** - * @ngdoc method - * @name ng.$filterProvider#register - * @methodOf ng.$filterProvider - * @description - * Register filter factory function. - * - * @param {String} name Name of the filter. - * @param {function} fn The filter factory function which is injectable. - */ - - -/** - * @ngdoc function - * @name ng.$filter - * @function - * @description - * Filters are used for formatting data displayed to the user. - * - * The general syntax in templates is as follows: - * - * {{ expression [| filter_name[:parameter_value] ... ] }} - * - * @param {String} name Name of the filter function to retrieve - * @return {Function} the filter function - */ -$FilterProvider.$inject = ['$provide']; -function $FilterProvider($provide) { - var suffix = 'Filter'; - - function register(name, factory) { - return $provide.factory(name + suffix, factory); - } - this.register = register; - - this.$get = ['$injector', function($injector) { - return function(name) { - return $injector.get(name + suffix); - } - }]; - - //////////////////////////////////////// - - register('currency', currencyFilter); - register('date', dateFilter); - register('filter', filterFilter); - register('json', jsonFilter); - register('limitTo', limitToFilter); - register('lowercase', lowercaseFilter); - register('number', numberFilter); - register('orderBy', orderByFilter); - register('uppercase', uppercaseFilter); -} - -/** - * @ngdoc filter - * @name ng.filter:filter - * @function - * - * @description - * Selects a subset of items from `array` and returns it as a new array. - * - * Note: This function is used to augment the `Array` type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * - * @param {Array} array The source array. - * @param {string|Object|function()} expression The predicate to be used for selecting items from - * `array`. - * - * Can be one of: - * - * - `string`: Predicate that results in a substring match using the value of `expression` - * string. All strings or objects with string properties in `array` that contain this string - * will be returned. The predicate can be negated by prefixing the string with `!`. - * - * - `Object`: A pattern object can be used to filter specific properties on objects contained - * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items - * which have property `name` containing "M" and property `phone` containing "1". A special - * property name `$` can be used (as in `{$:"text"}`) to accept a match against any - * property of the object. That's equivalent to the simple substring match with a `string` - * as described above. - * - * - `function`: A predicate function can be used to write arbitrary filters. The function is - * called for each element of `array`. The final result is an array of those elements that - * the predicate returned true for. - * - * @param {function(expected, actual)|true|undefined} comparator Comparator which is used in - * determining if the expected value (from the filter expression) and actual value (from - * the object in the array) should be considered a match. - * - * Can be one of: - * - * - `function(expected, actual)`: - * The function will be given the object value and the predicate value to compare and - * should return true if the item should be included in filtered result. - * - * - `true`: A shorthand for `function(expected, actual) { return angular.equals(expected, actual)}`. - * this is essentially strict comparison of expected and actual. - * - * - `false|undefined`: A short hand for a function which will look for a substring match in case - * insensitive way. - * - * @example - - -
- - Search: -
- - - - - -
NamePhone
{{friend.name}}{{friend.phone}}
-
- Any:
- Name only
- Phone only
- Equality
- - - - - - -
NamePhone
{{friend.name}}{{friend.phone}}
- - - it('should search across all fields when filtering with a string', function() { - input('searchText').enter('m'); - expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). - toEqual(['Mary', 'Mike', 'Adam']); - - input('searchText').enter('76'); - expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). - toEqual(['John', 'Julie']); - }); - - it('should search in specific fields when filtering with a predicate object', function() { - input('search.$').enter('i'); - expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')). - toEqual(['Mary', 'Mike', 'Julie', 'Juliette']); - }); - it('should use a equal comparison when comparator is true', function() { - input('search.name').enter('Julie'); - input('strict').check(); - expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')). - toEqual(['Julie']); - }); - - - */ -function filterFilter() { - return function(array, expression, comperator) { - if (!isArray(array)) return array; - var predicates = []; - predicates.check = function(value) { - for (var j = 0; j < predicates.length; j++) { - if(!predicates[j](value)) { - return false; - } - } - return true; - }; - switch(typeof comperator) { - case "function": - break; - case "boolean": - if(comperator == true) { - comperator = function(obj, text) { - return angular.equals(obj, text); - } - break; - } - default: - comperator = function(obj, text) { - text = (''+text).toLowerCase(); - return (''+obj).toLowerCase().indexOf(text) > -1 - }; - } - var search = function(obj, text){ - if (typeof text == 'string' && text.charAt(0) === '!') { - return !search(obj, text.substr(1)); - } - switch (typeof obj) { - case "boolean": - case "number": - case "string": - return comperator(obj, text); - case "object": - switch (typeof text) { - case "object": - return comperator(obj, text); - break; - default: - for ( var objKey in obj) { - if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { - return true; - } - } - break; - } - return false; - case "array": - for ( var i = 0; i < obj.length; i++) { - if (search(obj[i], text)) { - return true; - } - } - return false; - default: - return false; - } - }; - switch (typeof expression) { - case "boolean": - case "number": - case "string": - expression = {$:expression}; - case "object": - for (var key in expression) { - if (key == '$') { - (function() { - if (!expression[key]) return; - var path = key - predicates.push(function(value) { - return search(value, expression[path]); - }); - })(); - } else { - (function() { - if (!expression[key]) return; - var path = key; - predicates.push(function(value) { - return search(getter(value,path), expression[path]); - }); - })(); - } - } - break; - case 'function': - predicates.push(expression); - break; - default: - return array; - } - var filtered = []; - for ( var j = 0; j < array.length; j++) { - var value = array[j]; - if (predicates.check(value)) { - filtered.push(value); - } - } - return filtered; - } -} - -/** - * @ngdoc filter - * @name ng.filter:currency - * @function - * - * @description - * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default - * symbol for current locale is used. - * - * @param {number} amount Input to filter. - * @param {string=} symbol Currency symbol or identifier to be displayed. - * @returns {string} Formatted number. - * - * - * @example - - - -
-
- default currency symbol ($): {{amount | currency}}
- custom currency identifier (USD$): {{amount | currency:"USD$"}} -
-
- - it('should init with 1234.56', function() { - expect(binding('amount | currency')).toBe('$1,234.56'); - expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56'); - }); - it('should update', function() { - input('amount').enter('-1234'); - expect(binding('amount | currency')).toBe('($1,234.00)'); - expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)'); - }); - -
- */ -currencyFilter.$inject = ['$locale']; -function currencyFilter($locale) { - var formats = $locale.NUMBER_FORMATS; - return function(amount, currencySymbol){ - if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM; - return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2). - replace(/\u00A4/g, currencySymbol); - }; -} - -/** - * @ngdoc filter - * @name ng.filter:number - * @function - * - * @description - * Formats a number as text. - * - * If the input is not a number an empty string is returned. - * - * @param {number|string} number Number to format. - * @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to. - * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit. - * - * @example - - - -
- Enter number:
- Default formatting: {{val | number}}
- No fractions: {{val | number:0}}
- Negative number: {{-val | number:4}} -
-
- - it('should format numbers', function() { - expect(binding('val | number')).toBe('1,234.568'); - expect(binding('val | number:0')).toBe('1,235'); - expect(binding('-val | number:4')).toBe('-1,234.5679'); - }); - - it('should update', function() { - input('val').enter('3374.333'); - expect(binding('val | number')).toBe('3,374.333'); - expect(binding('val | number:0')).toBe('3,374'); - expect(binding('-val | number:4')).toBe('-3,374.3330'); - }); - -
- */ - - -numberFilter.$inject = ['$locale']; -function numberFilter($locale) { - var formats = $locale.NUMBER_FORMATS; - return function(number, fractionSize) { - return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, - fractionSize); - }; -} - -var DECIMAL_SEP = '.'; -function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { - if (isNaN(number) || !isFinite(number)) return ''; - - var isNegative = number < 0; - number = Math.abs(number); - var numStr = number + '', - formatedText = '', - parts = []; - - var hasExponent = false; - if (numStr.indexOf('e') !== -1) { - var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); - if (match && match[2] == '-' && match[3] > fractionSize + 1) { - numStr = '0'; - } else { - formatedText = numStr; - hasExponent = true; - } - } - - if (!hasExponent) { - var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; - - // determine fractionSize if it is not specified - if (isUndefined(fractionSize)) { - fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); - } - - var pow = Math.pow(10, fractionSize); - number = Math.round(number * pow) / pow; - var fraction = ('' + number).split(DECIMAL_SEP); - var whole = fraction[0]; - fraction = fraction[1] || ''; - - var pos = 0, - lgroup = pattern.lgSize, - group = pattern.gSize; - - if (whole.length >= (lgroup + group)) { - pos = whole.length - lgroup; - for (var i = 0; i < pos; i++) { - if ((pos - i)%group === 0 && i !== 0) { - formatedText += groupSep; - } - formatedText += whole.charAt(i); - } - } - - for (i = pos; i < whole.length; i++) { - if ((whole.length - i)%lgroup === 0 && i !== 0) { - formatedText += groupSep; - } - formatedText += whole.charAt(i); - } - - // format fraction part. - while(fraction.length < fractionSize) { - fraction += '0'; - } - - if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); - } - - parts.push(isNegative ? pattern.negPre : pattern.posPre); - parts.push(formatedText); - parts.push(isNegative ? pattern.negSuf : pattern.posSuf); - return parts.join(''); -} - -function padNumber(num, digits, trim) { - var neg = ''; - if (num < 0) { - neg = '-'; - num = -num; - } - num = '' + num; - while(num.length < digits) num = '0' + num; - if (trim) - num = num.substr(num.length - digits); - return neg + num; -} - - -function dateGetter(name, size, offset, trim) { - offset = offset || 0; - return function(date) { - var value = date['get' + name](); - if (offset > 0 || value > -offset) - value += offset; - if (value === 0 && offset == -12 ) value = 12; - return padNumber(value, size, trim); - }; -} - -function dateStrGetter(name, shortForm) { - return function(date, formats) { - var value = date['get' + name](); - var get = uppercase(shortForm ? ('SHORT' + name) : name); - - return formats[get][value]; - }; -} - -function timeZoneGetter(date) { - var zone = -1 * date.getTimezoneOffset(); - var paddedZone = (zone >= 0) ? "+" : ""; - - paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + - padNumber(Math.abs(zone % 60), 2); - - return paddedZone; -} - -function ampmGetter(date, formats) { - return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; -} - -var DATE_FORMATS = { - yyyy: dateGetter('FullYear', 4), - yy: dateGetter('FullYear', 2, 0, true), - y: dateGetter('FullYear', 1), - MMMM: dateStrGetter('Month'), - MMM: dateStrGetter('Month', true), - MM: dateGetter('Month', 2, 1), - M: dateGetter('Month', 1, 1), - dd: dateGetter('Date', 2), - d: dateGetter('Date', 1), - HH: dateGetter('Hours', 2), - H: dateGetter('Hours', 1), - hh: dateGetter('Hours', 2, -12), - h: dateGetter('Hours', 1, -12), - mm: dateGetter('Minutes', 2), - m: dateGetter('Minutes', 1), - ss: dateGetter('Seconds', 2), - s: dateGetter('Seconds', 1), - // while ISO 8601 requires fractions to be prefixed with `.` or `,` - // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions - sss: dateGetter('Milliseconds', 3), - EEEE: dateStrGetter('Day'), - EEE: dateStrGetter('Day', true), - a: ampmGetter, - Z: timeZoneGetter -}; - -var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, - NUMBER_STRING = /^\d+$/; - -/** - * @ngdoc filter - * @name ng.filter:date - * @function - * - * @description - * Formats `date` to a string based on the requested `format`. - * - * `format` string can be composed of the following elements: - * - * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) - * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) - * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) - * * `'MMMM'`: Month in year (January-December) - * * `'MMM'`: Month in year (Jan-Dec) - * * `'MM'`: Month in year, padded (01-12) - * * `'M'`: Month in year (1-12) - * * `'dd'`: Day in month, padded (01-31) - * * `'d'`: Day in month (1-31) - * * `'EEEE'`: Day in Week,(Sunday-Saturday) - * * `'EEE'`: Day in Week, (Sun-Sat) - * * `'HH'`: Hour in day, padded (00-23) - * * `'H'`: Hour in day (0-23) - * * `'hh'`: Hour in am/pm, padded (01-12) - * * `'h'`: Hour in am/pm, (1-12) - * * `'mm'`: Minute in hour, padded (00-59) - * * `'m'`: Minute in hour (0-59) - * * `'ss'`: Second in minute, padded (00-59) - * * `'s'`: Second in minute (0-59) - * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999) - * * `'a'`: am/pm marker - * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) - * - * `format` string can also be one of the following predefined - * {@link guide/i18n localizable formats}: - * - * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale - * (e.g. Sep 3, 2010 12:05:08 pm) - * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 pm) - * * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US locale - * (e.g. Friday, September 3, 2010) - * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010 - * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) - * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) - * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm) - * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm) - * - * `format` string can contain literal values. These need to be quoted with single quotes (e.g. - * `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence - * (e.g. `"h o''clock"`). - * - * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or - * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its - * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is - * specified in the string input, the time is considered to be in the local timezone. - * @param {string=} format Formatting rules (see Description). If not specified, - * `mediumDate` is used. - * @returns {string} Formatted string or the input if input is not recognized as date/millis. - * - * @example - - - {{1288323623006 | date:'medium'}}: - {{1288323623006 | date:'medium'}}
- {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}: - {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}
- {{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}: - {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}
-
- - it('should format date', function() { - expect(binding("1288323623006 | date:'medium'")). - toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); - expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")). - toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/); - expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")). - toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); - }); - -
- */ -dateFilter.$inject = ['$locale']; -function dateFilter($locale) { - - - var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; - // 1 2 3 4 5 6 7 8 9 10 11 - function jsonStringToDate(string) { - var match; - if (match = string.match(R_ISO8601_STR)) { - var date = new Date(0), - tzHour = 0, - tzMin = 0, - dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, - timeSetter = match[8] ? date.setUTCHours : date.setHours; - - if (match[9]) { - tzHour = int(match[9] + match[10]); - tzMin = int(match[9] + match[11]); - } - dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3])); - var h = int(match[4]||0) - tzHour; - var m = int(match[5]||0) - tzMin - var s = int(match[6]||0); - var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000); - timeSetter.call(date, h, m, s, ms); - return date; - } - return string; - } - - - return function(date, format) { - var text = '', - parts = [], - fn, match; - - format = format || 'mediumDate'; - format = $locale.DATETIME_FORMATS[format] || format; - if (isString(date)) { - if (NUMBER_STRING.test(date)) { - date = int(date); - } else { - date = jsonStringToDate(date); - } - } - - if (isNumber(date)) { - date = new Date(date); - } - - if (!isDate(date)) { - return date; - } - - while(format) { - match = DATE_FORMATS_SPLIT.exec(format); - if (match) { - parts = concat(parts, match, 1); - format = parts.pop(); - } else { - parts.push(format); - format = null; - } - } - - forEach(parts, function(value){ - fn = DATE_FORMATS[value]; - text += fn ? fn(date, $locale.DATETIME_FORMATS) - : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); - }); - - return text; - }; -} - - -/** - * @ngdoc filter - * @name ng.filter:json - * @function - * - * @description - * Allows you to convert a JavaScript object into JSON string. - * - * This filter is mostly useful for debugging. When using the double curly {{value}} notation - * the binding is automatically converted to JSON. - * - * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. - * @returns {string} JSON string. - * - * - * @example: - - -
{{ {'name':'value'} | json }}
-
- - it('should jsonify filtered objects', function() { - expect(binding("{'name':'value'}")).toMatch(/\{\n "name": ?"value"\n}/); - }); - -
- * - */ -function jsonFilter() { - return function(object) { - return toJson(object, true); - }; -} - - -/** - * @ngdoc filter - * @name ng.filter:lowercase - * @function - * @description - * Converts string to lowercase. - * @see angular.lowercase - */ -var lowercaseFilter = valueFn(lowercase); - - -/** - * @ngdoc filter - * @name ng.filter:uppercase - * @function - * @description - * Converts string to uppercase. - * @see angular.uppercase - */ -var uppercaseFilter = valueFn(uppercase); - -/** - * @ngdoc function - * @name ng.filter:limitTo - * @function - * - * @description - * Creates a new array or string containing only a specified number of elements. The elements - * are taken from either the beginning or the end of the source array or string, as specified by - * the value and sign (positive or negative) of `limit`. - * - * Note: This function is used to augment the `Array` type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * - * @param {Array|string} input Source array or string to be limited. - * @param {string|number} limit The length of the returned array or string. If the `limit` number - * is positive, `limit` number of items from the beginning of the source array/string are copied. - * If the number is negative, `limit` number of items from the end of the source array/string - * are copied. The `limit` will be trimmed if it exceeds `array.length` - * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array - * had less than `limit` elements. - * - * @example - - - -
- Limit {{numbers}} to: -

Output numbers: {{ numbers | limitTo:numLimit }}

- Limit {{letters}} to: -

Output letters: {{ letters | limitTo:letterLimit }}

-
-
- - it('should limit the number array to first three items', function() { - expect(element('.doc-example-live input[ng-model=numLimit]').val()).toBe('3'); - expect(element('.doc-example-live input[ng-model=letterLimit]').val()).toBe('3'); - expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3]'); - expect(binding('letters | limitTo:letterLimit')).toEqual('abc'); - }); - - it('should update the output when -3 is entered', function() { - input('numLimit').enter(-3); - input('letterLimit').enter(-3); - expect(binding('numbers | limitTo:numLimit')).toEqual('[7,8,9]'); - expect(binding('letters | limitTo:letterLimit')).toEqual('ghi'); - }); - - it('should not exceed the maximum size of input array', function() { - input('numLimit').enter(100); - input('letterLimit').enter(100); - expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3,4,5,6,7,8,9]'); - expect(binding('letters | limitTo:letterLimit')).toEqual('abcdefghi'); - }); - -
- */ -function limitToFilter(){ - return function(input, limit) { - if (!isArray(input) && !isString(input)) return input; - - limit = int(limit); - - if (isString(input)) { - //NaN check on limit - if (limit) { - return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length); - } else { - return ""; - } - } - - var out = [], - i, n; - - // if abs(limit) exceeds maximum length, trim it - if (limit > input.length) - limit = input.length; - else if (limit < -input.length) - limit = -input.length; - - if (limit > 0) { - i = 0; - n = limit; - } else { - i = input.length + limit; - n = input.length; - } - - for (; i} expression A predicate to be - * used by the comparator to determine the order of elements. - * - * Can be one of: - * - * - `function`: Getter function. The result of this function will be sorted using the - * `<`, `=`, `>` operator. - * - `string`: An Angular expression which evaluates to an object to order by, such as 'name' - * to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control - * ascending or descending sort order (for example, +name or -name). - * - `Array`: An array of function or string predicates. The first predicate in the array - * is used for sorting, but when two items are equivalent, the next predicate is used. - * - * @param {boolean=} reverse Reverse the order the array. - * @returns {Array} Sorted copy of the source array. - * - * @example - - - -
-
Sorting predicate = {{predicate}}; reverse = {{reverse}}
-
- [ unsorted ] - - - - - - - - - - - -
Name - (^)Phone NumberAge
{{friend.name}}{{friend.phone}}{{friend.age}}
-
-
- - it('should be reverse ordered by aged', function() { - expect(binding('predicate')).toBe('-age'); - expect(repeater('table.friend', 'friend in friends').column('friend.age')). - toEqual(['35', '29', '21', '19', '10']); - expect(repeater('table.friend', 'friend in friends').column('friend.name')). - toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']); - }); - - it('should reorder the table when user selects different predicate', function() { - element('.doc-example-live a:contains("Name")').click(); - expect(repeater('table.friend', 'friend in friends').column('friend.name')). - toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']); - expect(repeater('table.friend', 'friend in friends').column('friend.age')). - toEqual(['35', '10', '29', '19', '21']); - - element('.doc-example-live a:contains("Phone")').click(); - expect(repeater('table.friend', 'friend in friends').column('friend.phone')). - toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']); - expect(repeater('table.friend', 'friend in friends').column('friend.name')). - toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']); - }); - -
- */ -orderByFilter.$inject = ['$parse']; -function orderByFilter($parse){ - return function(array, sortPredicate, reverseOrder) { - if (!isArray(array)) return array; - if (!sortPredicate) return array; - sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate]; - sortPredicate = map(sortPredicate, function(predicate){ - var descending = false, get = predicate || identity; - if (isString(predicate)) { - if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { - descending = predicate.charAt(0) == '-'; - predicate = predicate.substring(1); - } - get = $parse(predicate); - } - return reverseComparator(function(a,b){ - return compare(get(a),get(b)); - }, descending); - }); - var arrayCopy = []; - for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); } - return arrayCopy.sort(reverseComparator(comparator, reverseOrder)); - - function comparator(o1, o2){ - for ( var i = 0; i < sortPredicate.length; i++) { - var comp = sortPredicate[i](o1, o2); - if (comp !== 0) return comp; - } - return 0; - } - function reverseComparator(comp, descending) { - return toBoolean(descending) - ? function(a,b){return comp(b,a);} - : comp; - } - function compare(v1, v2){ - var t1 = typeof v1; - var t2 = typeof v2; - if (t1 == t2) { - if (t1 == "string") v1 = v1.toLowerCase(); - if (t1 == "string") v2 = v2.toLowerCase(); - if (v1 === v2) return 0; - return v1 < v2 ? -1 : 1; - } else { - return t1 < t2 ? -1 : 1; - } - } - } -} - -function ngDirective(directive) { - if (isFunction(directive)) { - directive = { - link: directive - } - } - directive.restrict = directive.restrict || 'AC'; - return valueFn(directive); -} - -/** - * @ngdoc directive - * @name ng.directive:a - * @restrict E - * - * @description - * Modifies the default behavior of html A tag, so that the default action is prevented when href - * attribute is empty. - * - * The reasoning for this change is to allow easy creation of action links with `ngClick` directive - * without changing the location or causing page reloads, e.g.: - * `Save` - */ -var htmlAnchorDirective = valueFn({ - restrict: 'E', - compile: function(element, attr) { - - if (msie <= 8) { - - // turn link into a stylable link in IE - // but only if it doesn't have name attribute, in which case it's an anchor - if (!attr.href && !attr.name) { - attr.$set('href', ''); - } - - // add a comment node to anchors to workaround IE bug that causes element content to be reset - // to new attribute content if attribute is updated with value containing @ and element also - // contains value with @ - // see issue #1949 - element.append(document.createComment('IE fix')); - } - - return function(scope, element) { - element.bind('click', function(event){ - // if we have no href url, then don't navigate anywhere. - if (!element.attr('href')) { - event.preventDefault(); - } - }); - } - } -}); - -/** - * @ngdoc directive - * @name ng.directive:ngHref - * @restrict A - * - * @description - * Using Angular markup like {{hash}} in an href attribute makes - * the page open to a wrong URL, if the user clicks that link before - * angular has a chance to replace the {{hash}} with actual URL, the - * link will be broken and will most likely return a 404 error. - * The `ngHref` directive solves this problem. - * - * The buggy way to write it: - *
- * 
- * 
- * - * The correct way to write it: - *
- * 
- * 
- * - * @element A - * @param {template} ngHref any string which can contain `{{}}` markup. - * - * @example - * This example uses `link` variable inside `href` attribute: - - -
-
link 1 (link, don't reload)
- link 2 (link, don't reload)
- link 3 (link, reload!)
- anchor (link, don't reload)
- anchor (no link)
- link (link, change location) - - - it('should execute ng-click but not reload when href without value', function() { - element('#link-1').click(); - expect(input('value').val()).toEqual('1'); - expect(element('#link-1').attr('href')).toBe(""); - }); - - it('should execute ng-click but not reload when href empty string', function() { - element('#link-2').click(); - expect(input('value').val()).toEqual('2'); - expect(element('#link-2').attr('href')).toBe(""); - }); - - it('should execute ng-click and change url when ng-href specified', function() { - expect(element('#link-3').attr('href')).toBe("/123"); - - element('#link-3').click(); - expect(browser().window().path()).toEqual('/123'); - }); - - it('should execute ng-click but not reload when href empty string and name specified', function() { - element('#link-4').click(); - expect(input('value').val()).toEqual('4'); - expect(element('#link-4').attr('href')).toBe(''); - }); - - it('should execute ng-click but not reload when no href but name specified', function() { - element('#link-5').click(); - expect(input('value').val()).toEqual('5'); - expect(element('#link-5').attr('href')).toBe(undefined); - }); - - it('should only change url when only ng-href', function() { - input('value').enter('6'); - expect(element('#link-6').attr('href')).toBe('6'); - - element('#link-6').click(); - expect(browser().location().url()).toEqual('/6'); - }); - - - */ - -/** - * @ngdoc directive - * @name ng.directive:ngSrc - * @restrict A - * - * @description - * Using Angular markup like `{{hash}}` in a `src` attribute doesn't - * work right: The browser will fetch from the URL with the literal - * text `{{hash}}` until Angular replaces the expression inside - * `{{hash}}`. The `ngSrc` directive solves this problem. - * - * The buggy way to write it: - *
- * 
- * 
- * - * The correct way to write it: - *
- * 
- * 
- * - * @element IMG - * @param {template} ngSrc any string which can contain `{{}}` markup. - */ - -/** - * @ngdoc directive - * @name ng.directive:ngSrcset - * @restrict A - * - * @description - * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't - * work right: The browser will fetch from the URL with the literal - * text `{{hash}}` until Angular replaces the expression inside - * `{{hash}}`. The `ngSrcset` directive solves this problem. - * - * The buggy way to write it: - *
- * 
- * 
- * - * The correct way to write it: - *
- * 
- * 
- * - * @element IMG - * @param {template} ngSrcset any string which can contain `{{}}` markup. - */ - -/** - * @ngdoc directive - * @name ng.directive:ngDisabled - * @restrict A - * - * @description - * - * The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs: - *
- * 
- * - *
- *
- * - * The HTML specs do not require browsers to preserve the special attributes such as disabled. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce the `ngDisabled` directive. - * - * @example - - - Click me to toggle:
- -
- - it('should toggle button', function() { - expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy(); - input('checked').check(); - expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy(); - }); - -
- * - * @element INPUT - * @param {expression} ngDisabled Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngChecked - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as checked. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce the `ngChecked` directive. - * @example - - - Check me to check both:
- -
- - it('should check both checkBoxes', function() { - expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy(); - input('master').check(); - expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy(); - }); - -
- * - * @element INPUT - * @param {expression} ngChecked Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMultiple - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as multiple. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce the `ngMultiple` directive. - * - * @example - - - Check me check multiple:
- -
- - it('should toggle multiple', function() { - expect(element('.doc-example-live #select').prop('multiple')).toBeFalsy(); - input('checked').check(); - expect(element('.doc-example-live #select').prop('multiple')).toBeTruthy(); - }); - -
- * - * @element SELECT - * @param {expression} ngMultiple Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngReadonly - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as readonly. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce the `ngReadonly` directive. - * @example - - - Check me to make text readonly:
- -
- - it('should toggle readonly attr', function() { - expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy(); - input('checked').check(); - expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy(); - }); - -
- * - * @element INPUT - * @param {string} expression Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngSelected - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as selected. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduced the `ngSelected` directive. - * @example - - - Check me to select:
- -
- - it('should select Greetings!', function() { - expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy(); - input('selected').check(); - expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy(); - }); - -
- * - * @element OPTION - * @param {string} expression Angular expression that will be evaluated. - */ - -/** - * @ngdoc directive - * @name ng.directive:ngOpen - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as open. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce the `ngOpen` directive. - * - * @example - - - Check me check multiple:
-
- Show/Hide me -
-
- - it('should toggle open', function() { - expect(element('#details').prop('open')).toBeFalsy(); - input('open').check(); - expect(element('#details').prop('open')).toBeTruthy(); - }); - -
- * - * @element DETAILS - * @param {string} expression Angular expression that will be evaluated. - */ - -var ngAttributeAliasDirectives = {}; - - -// boolean attrs are evaluated -forEach(BOOLEAN_ATTR, function(propName, attrName) { - var normalized = directiveNormalize('ng-' + attrName); - ngAttributeAliasDirectives[normalized] = function() { - return { - priority: 100, - compile: function() { - return function(scope, element, attr) { - scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { - attr.$set(attrName, !!value); - }); - }; - } - }; - }; -}); - - -// ng-src, ng-srcset, ng-href are interpolated -forEach(['src', 'srcset', 'href'], function(attrName) { - var normalized = directiveNormalize('ng-' + attrName); - ngAttributeAliasDirectives[normalized] = function() { - return { - priority: 99, // it needs to run after the attributes are interpolated - link: function(scope, element, attr) { - attr.$observe(normalized, function(value) { - if (!value) - return; - - attr.$set(attrName, value); - - // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist - // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need - // to set the property as well to achieve the desired effect. - // we use attr[attrName] value since $set can sanitize the url. - if (msie) element.prop(attrName, attr[attrName]); - }); - } - }; - }; -}); - -var nullFormCtrl = { - $addControl: noop, - $removeControl: noop, - $setValidity: noop, - $setDirty: noop, - $setPristine: noop -}; - -/** - * @ngdoc object - * @name ng.directive:form.FormController - * - * @property {boolean} $pristine True if user has not interacted with the form yet. - * @property {boolean} $dirty True if user has already interacted with the form. - * @property {boolean} $valid True if all of the containing forms and controls are valid. - * @property {boolean} $invalid True if at least one containing control or form is invalid. - * - * @property {Object} $error Is an object hash, containing references to all invalid controls or - * forms, where: - * - * - keys are validation tokens (error names) — such as `required`, `url` or `email`), - * - values are arrays of controls or forms that are invalid with given error. - * - * @description - * `FormController` keeps track of all its controls and nested forms as well as state of them, - * such as being valid/invalid or dirty/pristine. - * - * Each {@link ng.directive:form form} directive creates an instance - * of `FormController`. - * - */ -//asks for $scope to fool the BC controller module -FormController.$inject = ['$element', '$attrs', '$scope']; -function FormController(element, attrs) { - var form = this, - parentForm = element.parent().controller('form') || nullFormCtrl, - invalidCount = 0, // used to easily determine if we are valid - errors = form.$error = {}, - controls = []; - - // init state - form.$name = attrs.name; - form.$dirty = false; - form.$pristine = true; - form.$valid = true; - form.$invalid = false; - - parentForm.$addControl(form); - - // Setup initial state of the control - element.addClass(PRISTINE_CLASS); - toggleValidCss(true); - - // convenience method for easy toggling of classes - function toggleValidCss(isValid, validationErrorKey) { - validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; - element. - removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). - addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); - } - - form.$addControl = function(control) { - controls.push(control); - - if (control.$name && !form.hasOwnProperty(control.$name)) { - form[control.$name] = control; - } - }; - - form.$removeControl = function(control) { - if (control.$name && form[control.$name] === control) { - delete form[control.$name]; - } - forEach(errors, function(queue, validationToken) { - form.$setValidity(validationToken, true, control); - }); - - arrayRemove(controls, control); - }; - - form.$setValidity = function(validationToken, isValid, control) { - var queue = errors[validationToken]; - - if (isValid) { - if (queue) { - arrayRemove(queue, control); - if (!queue.length) { - invalidCount--; - if (!invalidCount) { - toggleValidCss(isValid); - form.$valid = true; - form.$invalid = false; - } - errors[validationToken] = false; - toggleValidCss(true, validationToken); - parentForm.$setValidity(validationToken, true, form); - } - } - - } else { - if (!invalidCount) { - toggleValidCss(isValid); - } - if (queue) { - if (includes(queue, control)) return; - } else { - errors[validationToken] = queue = []; - invalidCount++; - toggleValidCss(false, validationToken); - parentForm.$setValidity(validationToken, false, form); - } - queue.push(control); - - form.$valid = false; - form.$invalid = true; - } - }; - - form.$setDirty = function() { - element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); - form.$dirty = true; - form.$pristine = false; - parentForm.$setDirty(); - }; - - /** - * @ngdoc function - * @name ng.directive:form.FormController#$setPristine - * @methodOf ng.directive:form.FormController - * - * @description - * Sets the form to its pristine state. - * - * This method can be called to remove the 'ng-dirty' class and set the form to its pristine - * state (ng-pristine class). This method will also propagate to all the controls contained - * in this form. - * - * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after - * saving or resetting it. - */ - form.$setPristine = function () { - element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS); - form.$dirty = false; - form.$pristine = true; - forEach(controls, function(control) { - control.$setPristine(); - }); - }; -} - - -/** - * @ngdoc directive - * @name ng.directive:ngForm - * @restrict EAC - * - * @description - * Nestable alias of {@link ng.directive:form `form`} directive. HTML - * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a - * sub-group of controls needs to be determined. - * - * @param {string=} name|ngForm Name of the form. If specified, the form controller will be published into - * related scope, under this name. - * - */ - - /** - * @ngdoc directive - * @name ng.directive:form - * @restrict E - * - * @description - * Directive that instantiates - * {@link ng.directive:form.FormController FormController}. - * - * If `name` attribute is specified, the form controller is published onto the current scope under - * this name. - * - * # Alias: {@link ng.directive:ngForm `ngForm`} - * - * In angular forms can be nested. This means that the outer form is valid when all of the child - * forms are valid as well. However browsers do not allow nesting of `
` elements, for this - * reason angular provides {@link ng.directive:ngForm `ngForm`} alias - * which behaves identical to `` but allows form nesting. - * - * - * # CSS classes - * - `ng-valid` Is set if the form is valid. - * - `ng-invalid` Is set if the form is invalid. - * - `ng-pristine` Is set if the form is pristine. - * - `ng-dirty` Is set if the form is dirty. - * - * - * # Submitting a form and preventing default action - * - * Since the role of forms in client-side Angular applications is different than in classical - * roundtrip apps, it is desirable for the browser not to translate the form submission into a full - * page reload that sends the data to the server. Instead some javascript logic should be triggered - * to handle the form submission in application specific way. - * - * For this reason, Angular prevents the default action (form submission to the server) unless the - * `` element has an `action` attribute specified. - * - * You can use one of the following two ways to specify what javascript method should be called when - * a form is submitted: - * - * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element - * - {@link ng.directive:ngClick ngClick} directive on the first - * button or input field of type submit (input[type=submit]) - * - * To prevent double execution of the handler, use only one of ngSubmit or ngClick directives. This - * is because of the following form submission rules coming from the html spec: - * - * - If a form has only one input field then hitting enter in this field triggers form submit - * (`ngSubmit`) - * - if a form has has 2+ input fields and no buttons or input[type=submit] then hitting enter - * doesn't trigger submit - * - if a form has one or more input fields and one or more buttons or input[type=submit] then - * hitting enter in any of the input fields will trigger the click handler on the *first* button or - * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) - * - * @param {string=} name Name of the form. If specified, the form controller will be published into - * related scope, under this name. - * - * @example - - - - - userType: - Required!
- userType = {{userType}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
- -
- - it('should initialize to model', function() { - expect(binding('userType')).toEqual('guest'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('userType').enter(''); - expect(binding('userType')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - -
- */ -var formDirectiveFactory = function(isNgForm) { - return ['$timeout', function($timeout) { - var formDirective = { - name: 'form', - restrict: 'E', - controller: FormController, - compile: function() { - return { - pre: function(scope, formElement, attr, controller) { - if (!attr.action) { - // we can't use jq events because if a form is destroyed during submission the default - // action is not prevented. see #1238 - // - // IE 9 is not affected because it doesn't fire a submit event and try to do a full - // page reload if the form was destroyed by submission of the form via a click handler - // on a button in the form. Looks like an IE9 specific bug. - var preventDefaultListener = function(event) { - event.preventDefault - ? event.preventDefault() - : event.returnValue = false; // IE - }; - - addEventListenerFn(formElement[0], 'submit', preventDefaultListener); - - // unregister the preventDefault listener so that we don't not leak memory but in a - // way that will achieve the prevention of the default action. - formElement.bind('$destroy', function() { - $timeout(function() { - removeEventListenerFn(formElement[0], 'submit', preventDefaultListener); - }, 0, false); - }); - } - - var parentFormCtrl = formElement.parent().controller('form'), - alias = attr.name || attr.ngForm; - - if (alias) { - scope[alias] = controller; - } - if (parentFormCtrl) { - formElement.bind('$destroy', function() { - parentFormCtrl.$removeControl(controller); - if (alias) { - scope[alias] = undefined; - } - extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards - }); - } - } - }; - } - }; - - return isNgForm ? extend(copy(formDirective), {restrict: 'EAC'}) : formDirective; - }]; -}; - -var formDirective = formDirectiveFactory(); -var ngFormDirective = formDirectiveFactory(true); - -var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; -var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/; -var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; - -var inputType = { - - /** - * @ngdoc inputType - * @name ng.directive:input.text - * - * @description - * Standard HTML text input with angular data binding. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Adds `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trimming the - * input. - * - * @example - - - -
- Single word: - - Required! - - Single word only! - - text = {{text}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
-
-
- - it('should initialize to model', function() { - expect(binding('text')).toEqual('guest'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('text').enter(''); - expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if multi word', function() { - input('text').enter('hello world'); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should not be trimmed', function() { - input('text').enter('untrimmed '); - expect(binding('text')).toEqual('untrimmed '); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - -
- */ - 'text': textInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.number - * - * @description - * Text input with number validation and transformation. Sets the `number` validation - * error if not a valid number. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- Number: - - Required! - - Not valid number! - value = {{value}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
-
-
- - it('should initialize to model', function() { - expect(binding('value')).toEqual('12'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('value').enter(''); - expect(binding('value')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if over max', function() { - input('value').enter('123'); - expect(binding('value')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - -
- */ - 'number': numberInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.url - * - * @description - * Text input with URL validation. Sets the `url` validation error key if the content is not a - * valid URL. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- URL: - - Required! - - Not valid url! - text = {{text}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
- myForm.$error.url = {{!!myForm.$error.url}}
-
-
- - it('should initialize to model', function() { - expect(binding('text')).toEqual('http://google.com'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('text').enter(''); - expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if not url', function() { - input('text').enter('xxx'); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - -
- */ - 'url': urlInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.email - * - * @description - * Text input with email validation. Sets the `email` validation error key if not a valid email - * address. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * - * @example - - - -
- Email: - - Required! - - Not valid email! - text = {{text}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
- myForm.$error.email = {{!!myForm.$error.email}}
-
-
- - it('should initialize to model', function() { - expect(binding('text')).toEqual('me@example.com'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('text').enter(''); - expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if not email', function() { - input('text').enter('xxx'); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - -
- */ - 'email': emailInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.radio - * - * @description - * HTML radio button. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string} value The value to which the expression should be set when selected. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- Red
- Green
- Blue
- color = {{color}}
-
-
- - it('should change state', function() { - expect(binding('color')).toEqual('blue'); - - input('color').select('red'); - expect(binding('color')).toEqual('red'); - }); - -
- */ - 'radio': radioInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.checkbox - * - * @description - * HTML checkbox. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ngTrueValue The value to which the expression should be set when selected. - * @param {string=} ngFalseValue The value to which the expression should be set when not selected. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- Value1:
- Value2:
- value1 = {{value1}}
- value2 = {{value2}}
-
-
- - it('should change state', function() { - expect(binding('value1')).toEqual('true'); - expect(binding('value2')).toEqual('YES'); - - input('value1').check(); - input('value2').check(); - expect(binding('value1')).toEqual('false'); - expect(binding('value2')).toEqual('NO'); - }); - -
- */ - 'checkbox': checkboxInputType, - - 'hidden': noop, - 'button': noop, - 'submit': noop, - 'reset': noop -}; - - -function isEmpty(value) { - return isUndefined(value) || value === '' || value === null || value !== value; -} - - -function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { - - var listener = function() { - var value = element.val(); - - // By default we will trim the value - // If the attribute ng-trim exists we will avoid trimming - // e.g. - if (toBoolean(attr.ngTrim || 'T')) { - value = trim(value); - } - - if (ctrl.$viewValue !== value) { - scope.$apply(function() { - ctrl.$setViewValue(value); - }); - } - }; - - // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the - // input event on backspace, delete or cut - if ($sniffer.hasEvent('input')) { - element.bind('input', listener); - } else { - var timeout; - - var deferListener = function() { - if (!timeout) { - timeout = $browser.defer(function() { - listener(); - timeout = null; - }); - } - }; - - element.bind('keydown', function(event) { - var key = event.keyCode; - - // ignore - // command modifiers arrows - if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; - - deferListener(); - }); - - // if user paste into input using mouse, we need "change" event to catch it - element.bind('change', listener); - - // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it - if ($sniffer.hasEvent('paste')) { - element.bind('paste cut', deferListener); - } - } - - - ctrl.$render = function() { - element.val(isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); - }; - - // pattern validator - var pattern = attr.ngPattern, - patternValidator, - match; - - var validate = function(regexp, value) { - if (isEmpty(value) || regexp.test(value)) { - ctrl.$setValidity('pattern', true); - return value; - } else { - ctrl.$setValidity('pattern', false); - return undefined; - } - }; - - if (pattern) { - match = pattern.match(/^\/(.*)\/([gim]*)$/); - if (match) { - pattern = new RegExp(match[1], match[2]); - patternValidator = function(value) { - return validate(pattern, value) - }; - } else { - patternValidator = function(value) { - var patternObj = scope.$eval(pattern); - - if (!patternObj || !patternObj.test) { - throw new Error('Expected ' + pattern + ' to be a RegExp but was ' + patternObj); - } - return validate(patternObj, value); - }; - } - - ctrl.$formatters.push(patternValidator); - ctrl.$parsers.push(patternValidator); - } - - // min length validator - if (attr.ngMinlength) { - var minlength = int(attr.ngMinlength); - var minLengthValidator = function(value) { - if (!isEmpty(value) && value.length < minlength) { - ctrl.$setValidity('minlength', false); - return undefined; - } else { - ctrl.$setValidity('minlength', true); - return value; - } - }; - - ctrl.$parsers.push(minLengthValidator); - ctrl.$formatters.push(minLengthValidator); - } - - // max length validator - if (attr.ngMaxlength) { - var maxlength = int(attr.ngMaxlength); - var maxLengthValidator = function(value) { - if (!isEmpty(value) && value.length > maxlength) { - ctrl.$setValidity('maxlength', false); - return undefined; - } else { - ctrl.$setValidity('maxlength', true); - return value; - } - }; - - ctrl.$parsers.push(maxLengthValidator); - ctrl.$formatters.push(maxLengthValidator); - } -} - -function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { - textInputType(scope, element, attr, ctrl, $sniffer, $browser); - - ctrl.$parsers.push(function(value) { - var empty = isEmpty(value); - if (empty || NUMBER_REGEXP.test(value)) { - ctrl.$setValidity('number', true); - return value === '' ? null : (empty ? value : parseFloat(value)); - } else { - ctrl.$setValidity('number', false); - return undefined; - } - }); - - ctrl.$formatters.push(function(value) { - return isEmpty(value) ? '' : '' + value; - }); - - if (attr.min) { - var min = parseFloat(attr.min); - var minValidator = function(value) { - if (!isEmpty(value) && value < min) { - ctrl.$setValidity('min', false); - return undefined; - } else { - ctrl.$setValidity('min', true); - return value; - } - }; - - ctrl.$parsers.push(minValidator); - ctrl.$formatters.push(minValidator); - } - - if (attr.max) { - var max = parseFloat(attr.max); - var maxValidator = function(value) { - if (!isEmpty(value) && value > max) { - ctrl.$setValidity('max', false); - return undefined; - } else { - ctrl.$setValidity('max', true); - return value; - } - }; - - ctrl.$parsers.push(maxValidator); - ctrl.$formatters.push(maxValidator); - } - - ctrl.$formatters.push(function(value) { - - if (isEmpty(value) || isNumber(value)) { - ctrl.$setValidity('number', true); - return value; - } else { - ctrl.$setValidity('number', false); - return undefined; - } - }); -} - -function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { - textInputType(scope, element, attr, ctrl, $sniffer, $browser); - - var urlValidator = function(value) { - if (isEmpty(value) || URL_REGEXP.test(value)) { - ctrl.$setValidity('url', true); - return value; - } else { - ctrl.$setValidity('url', false); - return undefined; - } - }; - - ctrl.$formatters.push(urlValidator); - ctrl.$parsers.push(urlValidator); -} - -function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { - textInputType(scope, element, attr, ctrl, $sniffer, $browser); - - var emailValidator = function(value) { - if (isEmpty(value) || EMAIL_REGEXP.test(value)) { - ctrl.$setValidity('email', true); - return value; - } else { - ctrl.$setValidity('email', false); - return undefined; - } - }; - - ctrl.$formatters.push(emailValidator); - ctrl.$parsers.push(emailValidator); -} - -function radioInputType(scope, element, attr, ctrl) { - // make the name unique, if not defined - if (isUndefined(attr.name)) { - element.attr('name', nextUid()); - } - - element.bind('click', function() { - if (element[0].checked) { - scope.$apply(function() { - ctrl.$setViewValue(attr.value); - }); - } - }); - - ctrl.$render = function() { - var value = attr.value; - element[0].checked = (value == ctrl.$viewValue); - }; - - attr.$observe('value', ctrl.$render); -} - -function checkboxInputType(scope, element, attr, ctrl) { - var trueValue = attr.ngTrueValue, - falseValue = attr.ngFalseValue; - - if (!isString(trueValue)) trueValue = true; - if (!isString(falseValue)) falseValue = false; - - element.bind('click', function() { - scope.$apply(function() { - ctrl.$setViewValue(element[0].checked); - }); - }); - - ctrl.$render = function() { - element[0].checked = ctrl.$viewValue; - }; - - ctrl.$formatters.push(function(value) { - return value === trueValue; - }); - - ctrl.$parsers.push(function(value) { - return value ? trueValue : falseValue; - }); -} - - -/** - * @ngdoc directive - * @name ng.directive:textarea - * @restrict E - * - * @description - * HTML textarea element control with angular data-binding. The data-binding and validation - * properties of this element are exactly the same as those of the - * {@link ng.directive:input input element}. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - */ - - -/** - * @ngdoc directive - * @name ng.directive:input - * @restrict E - * - * @description - * HTML input element control with angular data-binding. Input control follows HTML5 input types - * and polyfills the HTML5 validation behavior for older browsers. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {boolean=} ngRequired Sets `required` attribute if set to true - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
-
- User name: - - Required!
- Last name: - - Too short! - - Too long!
-
-
- user = {{user}}
- myForm.userName.$valid = {{myForm.userName.$valid}}
- myForm.userName.$error = {{myForm.userName.$error}}
- myForm.lastName.$valid = {{myForm.lastName.$valid}}
- myForm.lastName.$error = {{myForm.lastName.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
- myForm.$error.minlength = {{!!myForm.$error.minlength}}
- myForm.$error.maxlength = {{!!myForm.$error.maxlength}}
-
-
- - it('should initialize to model', function() { - expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}'); - expect(binding('myForm.userName.$valid')).toEqual('true'); - expect(binding('myForm.$valid')).toEqual('true'); - }); - - it('should be invalid if empty when required', function() { - input('user.name').enter(''); - expect(binding('user')).toEqual('{"last":"visitor"}'); - expect(binding('myForm.userName.$valid')).toEqual('false'); - expect(binding('myForm.$valid')).toEqual('false'); - }); - - it('should be valid if empty when min length is set', function() { - input('user.last').enter(''); - expect(binding('user')).toEqual('{"name":"guest","last":""}'); - expect(binding('myForm.lastName.$valid')).toEqual('true'); - expect(binding('myForm.$valid')).toEqual('true'); - }); - - it('should be invalid if less than required min length', function() { - input('user.last').enter('xx'); - expect(binding('user')).toEqual('{"name":"guest"}'); - expect(binding('myForm.lastName.$valid')).toEqual('false'); - expect(binding('myForm.lastName.$error')).toMatch(/minlength/); - expect(binding('myForm.$valid')).toEqual('false'); - }); - - it('should be invalid if longer than max length', function() { - input('user.last').enter('some ridiculously long name'); - expect(binding('user')) - .toEqual('{"name":"guest"}'); - expect(binding('myForm.lastName.$valid')).toEqual('false'); - expect(binding('myForm.lastName.$error')).toMatch(/maxlength/); - expect(binding('myForm.$valid')).toEqual('false'); - }); - -
- */ -var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) { - return { - restrict: 'E', - require: '?ngModel', - link: function(scope, element, attr, ctrl) { - if (ctrl) { - (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer, - $browser); - } - } - }; -}]; - -var VALID_CLASS = 'ng-valid', - INVALID_CLASS = 'ng-invalid', - PRISTINE_CLASS = 'ng-pristine', - DIRTY_CLASS = 'ng-dirty'; - -/** - * @ngdoc object - * @name ng.directive:ngModel.NgModelController - * - * @property {string} $viewValue Actual string value in the view. - * @property {*} $modelValue The value in the model, that the control is bound to. - * @property {Array.} $parsers Whenever the control reads value from the DOM, it executes - * all of these functions to sanitize / convert the value as well as validate. - * - * @property {Array.} $formatters Whenever the model value changes, it executes all of - * these functions to convert the value as well as validate. - * - * @property {Object} $error An object hash with all errors as keys. - * - * @property {boolean} $pristine True if user has not interacted with the control yet. - * @property {boolean} $dirty True if user has already interacted with the control. - * @property {boolean} $valid True if there is no error. - * @property {boolean} $invalid True if at least one error on the control. - * - * @description - * - * `NgModelController` provides API for the `ng-model` directive. The controller contains - * services for data-binding, validation, CSS update, value formatting and parsing. It - * specifically does not contain any logic which deals with DOM rendering or listening to - * DOM events. The `NgModelController` is meant to be extended by other directives where, the - * directive provides DOM manipulation and the `NgModelController` provides the data-binding. - * - * This example shows how to use `NgModelController` with a custom control to achieve - * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) - * collaborate together to achieve the desired result. - * - * - - [contenteditable] { - border: 1px solid black; - background-color: white; - min-height: 20px; - } - - .ng-invalid { - border: 1px solid red; - } - - - - angular.module('customControl', []). - directive('contenteditable', function() { - return { - restrict: 'A', // only activate on element attribute - require: '?ngModel', // get a hold of NgModelController - link: function(scope, element, attrs, ngModel) { - if(!ngModel) return; // do nothing if no ng-model - - // Specify how UI should be updated - ngModel.$render = function() { - element.html(ngModel.$viewValue || ''); - }; - - // Listen for change events to enable binding - element.bind('blur keyup change', function() { - scope.$apply(read); - }); - read(); // initialize - - // Write data to the model - function read() { - ngModel.$setViewValue(element.html()); - } - } - }; - }); - - -
-
Change me!
- Required! -
- -
-
- - it('should data-bind and become invalid', function() { - var contentEditable = element('[contenteditable]'); - - expect(contentEditable.text()).toEqual('Change me!'); - input('userContent').enter(''); - expect(contentEditable.text()).toEqual(''); - expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/); - }); - - *
- * - */ -var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', - function($scope, $exceptionHandler, $attr, $element, $parse) { - this.$viewValue = Number.NaN; - this.$modelValue = Number.NaN; - this.$parsers = []; - this.$formatters = []; - this.$viewChangeListeners = []; - this.$pristine = true; - this.$dirty = false; - this.$valid = true; - this.$invalid = false; - this.$name = $attr.name; - - var ngModelGet = $parse($attr.ngModel), - ngModelSet = ngModelGet.assign; - - if (!ngModelSet) { - throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + $attr.ngModel + - ' (' + startingTag($element) + ')'); - } - - /** - * @ngdoc function - * @name ng.directive:ngModel.NgModelController#$render - * @methodOf ng.directive:ngModel.NgModelController - * - * @description - * Called when the view needs to be updated. It is expected that the user of the ng-model - * directive will implement this method. - */ - this.$render = noop; - - var parentForm = $element.inheritedData('$formController') || nullFormCtrl, - invalidCount = 0, // used to easily determine if we are valid - $error = this.$error = {}; // keep invalid keys here - - - // Setup initial state of the control - $element.addClass(PRISTINE_CLASS); - toggleValidCss(true); - - // convenience method for easy toggling of classes - function toggleValidCss(isValid, validationErrorKey) { - validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; - $element. - removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). - addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); - } - - /** - * @ngdoc function - * @name ng.directive:ngModel.NgModelController#$setValidity - * @methodOf ng.directive:ngModel.NgModelController - * - * @description - * Change the validity state, and notifies the form when the control changes validity. (i.e. it - * does not notify form if given validator is already marked as invalid). - * - * This method should be called by validators - i.e. the parser or formatter functions. - * - * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign - * to `$error[validationErrorKey]=isValid` so that it is available for data-binding. - * The `validationErrorKey` should be in camelCase and will get converted into dash-case - * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` - * class and can be bound to as `{{someForm.someControl.$error.myError}}` . - * @param {boolean} isValid Whether the current state is valid (true) or invalid (false). - */ - this.$setValidity = function(validationErrorKey, isValid) { - if ($error[validationErrorKey] === !isValid) return; - - if (isValid) { - if ($error[validationErrorKey]) invalidCount--; - if (!invalidCount) { - toggleValidCss(true); - this.$valid = true; - this.$invalid = false; - } - } else { - toggleValidCss(false); - this.$invalid = true; - this.$valid = false; - invalidCount++; - } - - $error[validationErrorKey] = !isValid; - toggleValidCss(isValid, validationErrorKey); - - parentForm.$setValidity(validationErrorKey, isValid, this); - }; - - /** - * @ngdoc function - * @name ng.directive:ngModel.NgModelController#$setPristine - * @methodOf ng.directive:ngModel.NgModelController - * - * @description - * Sets the control to its pristine state. - * - * This method can be called to remove the 'ng-dirty' class and set the control to its pristine - * state (ng-pristine class). - */ - this.$setPristine = function () { - this.$dirty = false; - this.$pristine = true; - $element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS); - }; - - /** - * @ngdoc function - * @name ng.directive:ngModel.NgModelController#$setViewValue - * @methodOf ng.directive:ngModel.NgModelController - * - * @description - * Read a value from view. - * - * This method should be called from within a DOM event handler. - * For example {@link ng.directive:input input} or - * {@link ng.directive:select select} directives call it. - * - * It internally calls all `parsers` and if resulted value is valid, updates the model and - * calls all registered change listeners. - * - * @param {string} value Value from the view. - */ - this.$setViewValue = function(value) { - this.$viewValue = value; - - // change to dirty - if (this.$pristine) { - this.$dirty = true; - this.$pristine = false; - $element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); - parentForm.$setDirty(); - } - - forEach(this.$parsers, function(fn) { - value = fn(value); - }); - - if (this.$modelValue !== value) { - this.$modelValue = value; - ngModelSet($scope, value); - forEach(this.$viewChangeListeners, function(listener) { - try { - listener(); - } catch(e) { - $exceptionHandler(e); - } - }) - } - }; - - // model -> value - var ctrl = this; - - $scope.$watch(function ngModelWatch() { - var value = ngModelGet($scope); - - // if scope model value and ngModel value are out of sync - if (ctrl.$modelValue !== value) { - - var formatters = ctrl.$formatters, - idx = formatters.length; - - ctrl.$modelValue = value; - while(idx--) { - value = formatters[idx](value); - } - - if (ctrl.$viewValue !== value) { - ctrl.$viewValue = value; - ctrl.$render(); - } - } - }); -}]; - - -/** - * @ngdoc directive - * @name ng.directive:ngModel - * - * @element input - * - * @description - * Is directive that tells Angular to do two-way data binding. It works together with `input`, - * `select`, `textarea`. You can easily write your own directives to use `ngModel` as well. - * - * `ngModel` is responsible for: - * - * - binding the view into the model, which other directives such as `input`, `textarea` or `select` - * require, - * - providing validation behavior (i.e. required, number, email, url), - * - keeping state of the control (valid/invalid, dirty/pristine, validation errors), - * - setting related css class onto the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`), - * - register the control with parent {@link ng.directive:form form}. - * - * For basic examples, how to use `ngModel`, see: - * - * - {@link ng.directive:input input} - * - {@link ng.directive:input.text text} - * - {@link ng.directive:input.checkbox checkbox} - * - {@link ng.directive:input.radio radio} - * - {@link ng.directive:input.number number} - * - {@link ng.directive:input.email email} - * - {@link ng.directive:input.url url} - * - {@link ng.directive:select select} - * - {@link ng.directive:textarea textarea} - * - */ -var ngModelDirective = function() { - return { - require: ['ngModel', '^?form'], - controller: NgModelController, - link: function(scope, element, attr, ctrls) { - // notify others, especially parent forms - - var modelCtrl = ctrls[0], - formCtrl = ctrls[1] || nullFormCtrl; - - formCtrl.$addControl(modelCtrl); - - element.bind('$destroy', function() { - formCtrl.$removeControl(modelCtrl); - }); - } - }; -}; - - -/** - * @ngdoc directive - * @name ng.directive:ngChange - * @restrict E - * - * @description - * Evaluate given expression when user changes the input. - * The expression is not evaluated when the value change is coming from the model. - * - * Note, this directive requires `ngModel` to be present. - * - * @element input - * - * @example - * - * - * - *
- * - * - *
- * debug = {{confirmed}}
- * counter = {{counter}} - *
- *
- * - * it('should evaluate the expression if changing from view', function() { - * expect(binding('counter')).toEqual('0'); - * element('#ng-change-example1').click(); - * expect(binding('counter')).toEqual('1'); - * expect(binding('confirmed')).toEqual('true'); - * }); - * - * it('should not evaluate the expression if changing from model', function() { - * element('#ng-change-example2').click(); - * expect(binding('counter')).toEqual('0'); - * expect(binding('confirmed')).toEqual('true'); - * }); - * - *
- */ -var ngChangeDirective = valueFn({ - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - ctrl.$viewChangeListeners.push(function() { - scope.$eval(attr.ngChange); - }); - } -}); - - -var requiredDirective = function() { - return { - require: '?ngModel', - link: function(scope, elm, attr, ctrl) { - if (!ctrl) return; - attr.required = true; // force truthy in case we are on non input element - - var validator = function(value) { - if (attr.required && (isEmpty(value) || value === false)) { - ctrl.$setValidity('required', false); - return; - } else { - ctrl.$setValidity('required', true); - return value; - } - }; - - ctrl.$formatters.push(validator); - ctrl.$parsers.unshift(validator); - - attr.$observe('required', function() { - validator(ctrl.$viewValue); - }); - } - }; -}; - - -/** - * @ngdoc directive - * @name ng.directive:ngList - * - * @description - * Text input that converts between comma-separated string into an array of strings. - * - * @element input - * @param {string=} ngList optional delimiter that should be used to split the value. If - * specified in form `/something/` then the value will be converted into a regular expression. - * - * @example - - - -
- List: - - Required! - names = {{names}}
- myForm.namesInput.$valid = {{myForm.namesInput.$valid}}
- myForm.namesInput.$error = {{myForm.namesInput.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
-
-
- - it('should initialize to model', function() { - expect(binding('names')).toEqual('["igor","misko","vojta"]'); - expect(binding('myForm.namesInput.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('names').enter(''); - expect(binding('names')).toEqual('[]'); - expect(binding('myForm.namesInput.$valid')).toEqual('false'); - }); - -
- */ -var ngListDirective = function() { - return { - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - var match = /\/(.*)\//.exec(attr.ngList), - separator = match && new RegExp(match[1]) || attr.ngList || ','; - - var parse = function(viewValue) { - var list = []; - - if (viewValue) { - forEach(viewValue.split(separator), function(value) { - if (value) list.push(trim(value)); - }); - } - - return list; - }; - - ctrl.$parsers.push(parse); - ctrl.$formatters.push(function(value) { - if (isArray(value)) { - return value.join(', '); - } - - return undefined; - }); - } - }; -}; - - -var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; - -var ngValueDirective = function() { - return { - priority: 100, - compile: function(tpl, tplAttr) { - if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { - return function(scope, elm, attr) { - attr.$set('value', scope.$eval(attr.ngValue)); - }; - } else { - return function(scope, elm, attr) { - scope.$watch(attr.ngValue, function valueWatchAction(value) { - attr.$set('value', value, false); - }); - }; - } - } - }; -}; - -/** - * @ngdoc directive - * @name ng.directive:ngBind - * - * @description - * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element - * with the value of a given expression, and to update the text content when the value of that - * expression changes. - * - * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like - * `{{ expression }}` which is similar but less verbose. - * - * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when - * it's desirable to put bindings into template that is momentarily displayed by the browser in its - * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the - * bindings invisible to the user while the page is loading. - * - * An alternative solution to this problem would be using the - * {@link ng.directive:ngCloak ngCloak} directive. - * - * - * @element ANY - * @param {expression} ngBind {@link guide/expression Expression} to evaluate. - * - * @example - * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. - - - -
- Enter name:
- Hello ! -
-
- - it('should check ng-bind', function() { - expect(using('.doc-example-live').binding('name')).toBe('Whirled'); - using('.doc-example-live').input('name').enter('world'); - expect(using('.doc-example-live').binding('name')).toBe('world'); - }); - -
- */ -var ngBindDirective = ngDirective(function(scope, element, attr) { - element.addClass('ng-binding').data('$binding', attr.ngBind); - scope.$watch(attr.ngBind, function ngBindWatchAction(value) { - element.text(value == undefined ? '' : value); - }); -}); - - -/** - * @ngdoc directive - * @name ng.directive:ngBindTemplate - * - * @description - * The `ngBindTemplate` directive specifies that the element - * text should be replaced with the template in ngBindTemplate. - * Unlike ngBind the ngBindTemplate can contain multiple `{{` `}}` - * expressions. (This is required since some HTML elements - * can not have SPAN elements such as TITLE, or OPTION to name a few.) - * - * @element ANY - * @param {string} ngBindTemplate template of form - * {{ expression }} to eval. - * - * @example - * Try it here: enter text in text box and watch the greeting change. - - - -
- Salutation:
- Name:
-

-       
-
- - it('should check ng-bind', function() { - expect(using('.doc-example-live').binding('salutation')). - toBe('Hello'); - expect(using('.doc-example-live').binding('name')). - toBe('World'); - using('.doc-example-live').input('salutation').enter('Greetings'); - using('.doc-example-live').input('name').enter('user'); - expect(using('.doc-example-live').binding('salutation')). - toBe('Greetings'); - expect(using('.doc-example-live').binding('name')). - toBe('user'); - }); - -
- */ -var ngBindTemplateDirective = ['$interpolate', function($interpolate) { - return function(scope, element, attr) { - // TODO: move this to scenario runner - var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); - element.addClass('ng-binding').data('$binding', interpolateFn); - attr.$observe('ngBindTemplate', function(value) { - element.text(value); - }); - } -}]; - - -/** - * @ngdoc directive - * @name ng.directive:ngBindHtmlUnsafe - * - * @description - * Creates a binding that will innerHTML the result of evaluating the `expression` into the current - * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if - * {@link ngSanitize.directive:ngBindHtml ngBindHtml} directive is too - * restrictive and when you absolutely trust the source of the content you are binding to. - * - * See {@link ngSanitize.$sanitize $sanitize} docs for examples. - * - * @element ANY - * @param {expression} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate. - */ -var ngBindHtmlUnsafeDirective = [function() { - return function(scope, element, attr) { - element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe); - scope.$watch(attr.ngBindHtmlUnsafe, function ngBindHtmlUnsafeWatchAction(value) { - element.html(value || ''); - }); - }; -}]; - -function classDirective(name, selector) { - name = 'ngClass' + name; - return ngDirective(function(scope, element, attr) { - var oldVal = undefined; - - scope.$watch(attr[name], ngClassWatchAction, true); - - attr.$observe('class', function(value) { - var ngClass = scope.$eval(attr[name]); - ngClassWatchAction(ngClass, ngClass); - }); - - - if (name !== 'ngClass') { - scope.$watch('$index', function($index, old$index) { - var mod = $index & 1; - if (mod !== old$index & 1) { - if (mod === selector) { - addClass(scope.$eval(attr[name])); - } else { - removeClass(scope.$eval(attr[name])); - } - } - }); - } - - - function ngClassWatchAction(newVal) { - if (selector === true || scope.$index % 2 === selector) { - if (oldVal && !equals(newVal,oldVal)) { - removeClass(oldVal); - } - addClass(newVal); - } - oldVal = copy(newVal); - } - - - function removeClass(classVal) { - if (isObject(classVal) && !isArray(classVal)) { - classVal = map(classVal, function(v, k) { if (v) return k }); - } - element.removeClass(isArray(classVal) ? classVal.join(' ') : classVal); - } - - - function addClass(classVal) { - if (isObject(classVal) && !isArray(classVal)) { - classVal = map(classVal, function(v, k) { if (v) return k }); - } - if (classVal) { - element.addClass(isArray(classVal) ? classVal.join(' ') : classVal); - } - } - }); -} - -/** - * @ngdoc directive - * @name ng.directive:ngClass - * - * @description - * The `ngClass` allows you to set CSS class on HTML element dynamically by databinding an - * expression that represents all classes to be added. - * - * The directive won't add duplicate classes if a particular class was already set. - * - * When the expression changes, the previously added classes are removed and only then the - * new classes are added. - * - * @element ANY - * @param {expression} ngClass {@link guide/expression Expression} to eval. The result - * of the evaluation can be a string representing space delimited class - * names, an array, or a map of class names to boolean values. - * - * @example - - - - -
- Sample Text -
- - .my-class { - color: red; - } - - - it('should check ng-class', function() { - expect(element('.doc-example-live span').prop('className')).not(). - toMatch(/my-class/); - - using('.doc-example-live').element(':button:first').click(); - - expect(element('.doc-example-live span').prop('className')). - toMatch(/my-class/); - - using('.doc-example-live').element(':button:last').click(); - - expect(element('.doc-example-live span').prop('className')).not(). - toMatch(/my-class/); - }); - -
- */ -var ngClassDirective = classDirective('', true); - -/** - * @ngdoc directive - * @name ng.directive:ngClassOdd - * - * @description - * The `ngClassOdd` and `ngClassEven` directives work exactly as - * {@link ng.directive:ngClass ngClass}, except it works in - * conjunction with `ngRepeat` and takes affect only on odd (even) rows. - * - * This directive can be applied only within a scope of an - * {@link ng.directive:ngRepeat ngRepeat}. - * - * @element ANY - * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result - * of the evaluation can be a string representing space delimited class names or an array. - * - * @example - - -
    -
  1. - - {{name}} - -
  2. -
-
- - .odd { - color: red; - } - .even { - color: blue; - } - - - it('should check ng-class-odd and ng-class-even', function() { - expect(element('.doc-example-live li:first span').prop('className')). - toMatch(/odd/); - expect(element('.doc-example-live li:last span').prop('className')). - toMatch(/even/); - }); - -
- */ -var ngClassOddDirective = classDirective('Odd', 0); - -/** - * @ngdoc directive - * @name ng.directive:ngClassEven - * - * @description - * The `ngClassOdd` and `ngClassEven` directives work exactly as - * {@link ng.directive:ngClass ngClass}, except it works in - * conjunction with `ngRepeat` and takes affect only on odd (even) rows. - * - * This directive can be applied only within a scope of an - * {@link ng.directive:ngRepeat ngRepeat}. - * - * @element ANY - * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The - * result of the evaluation can be a string representing space delimited class names or an array. - * - * @example - - -
    -
  1. - - {{name}}       - -
  2. -
-
- - .odd { - color: red; - } - .even { - color: blue; - } - - - it('should check ng-class-odd and ng-class-even', function() { - expect(element('.doc-example-live li:first span').prop('className')). - toMatch(/odd/); - expect(element('.doc-example-live li:last span').prop('className')). - toMatch(/even/); - }); - -
- */ -var ngClassEvenDirective = classDirective('Even', 1); - -/** - * @ngdoc directive - * @name ng.directive:ngCloak - * - * @description - * The `ngCloak` directive is used to prevent the Angular html template from being briefly - * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this - * directive to avoid the undesirable flicker effect caused by the html template display. - * - * The directive can be applied to the `` element, but typically a fine-grained application is - * preferred in order to benefit from progressive rendering of the browser view. - * - * `ngCloak` works in cooperation with a css rule that is embedded within `angular.js` and - * `angular.min.js` files. Following is the css rule: - * - *
- * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
- *   display: none;
- * }
- * 
- * - * When this css rule is loaded by the browser, all html elements (including their children) that - * are tagged with the `ng-cloak` directive are hidden. When Angular comes across this directive - * during the compilation of the template it deletes the `ngCloak` element attribute, which - * makes the compiled element visible. - * - * For the best result, `angular.js` script must be loaded in the head section of the html file; - * alternatively, the css rule (above) must be included in the external stylesheet of the - * application. - * - * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they - * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css - * class `ngCloak` in addition to `ngCloak` directive as shown in the example below. - * - * @element ANY - * - * @example - - -
{{ 'hello' }}
-
{{ 'hello IE7' }}
-
- - it('should remove the template directive and css class', function() { - expect(element('.doc-example-live #template1').attr('ng-cloak')). - not().toBeDefined(); - expect(element('.doc-example-live #template2').attr('ng-cloak')). - not().toBeDefined(); - }); - -
- * - */ -var ngCloakDirective = ngDirective({ - compile: function(element, attr) { - attr.$set('ngCloak', undefined); - element.removeClass('ng-cloak'); - } -}); - -/** - * @ngdoc directive - * @name ng.directive:ngController - * - * @description - * The `ngController` directive assigns behavior to a scope. This is a key aspect of how angular - * supports the principles behind the Model-View-Controller design pattern. - * - * MVC components in angular: - * - * * Model — The Model is data in scope properties; scopes are attached to the DOM. - * * View — The template (HTML with data bindings) is rendered into the View. - * * Controller — The `ngController` directive specifies a Controller class; the class has - * methods that typically express the business logic behind the application. - * - * Note that an alternative way to define controllers is via the {@link ng.$route $route} service. - * - * @element ANY - * @scope - * @param {expression} ngController Name of a globally accessible constructor function or an - * {@link guide/expression expression} that on the current scope evaluates to a - * constructor function. The controller instance can further be published into the scope - * by adding `as localName` the controller name attribute. - * - * @example - * Here is a simple form for editing user contact information. Adding, removing, clearing, and - * greeting are methods declared on the controller (see source tab). These methods can - * easily be called from the angular markup. Notice that the scope becomes the `this` for the - * controller's instance. This allows for easy access to the view data from the controller. Also - * notice that any changes to the data are automatically reflected in the View without the need - * for a manual update. The example is included in two different declaration styles based on - * your style preferences. - - - -
- Name: - [ greet ]
- Contact: -
    -
  • - - - [ clear - | X ] -
  • -
  • [ add ]
  • -
-
-
- - it('should check controller', function() { - expect(element('.doc-example-live div>:input').val()).toBe('John Smith'); - expect(element('.doc-example-live li:nth-child(1) input').val()) - .toBe('408 555 1212'); - expect(element('.doc-example-live li:nth-child(2) input').val()) - .toBe('john.smith@example.org'); - - element('.doc-example-live li:first a:contains("clear")').click(); - expect(element('.doc-example-live li:first input').val()).toBe(''); - - element('.doc-example-live li:last a:contains("add")').click(); - expect(element('.doc-example-live li:nth-child(3) input').val()) - .toBe('yourname@example.org'); - }); - -
- - - - - - -
- Name: - [ greet ]
- Contact: -
    -
  • - - - [ clear - | X ] -
  • -
  • [ add ]
  • -
-
-
- - it('should check controller', function() { - expect(element('.doc-example-live div>:input').val()).toBe('John Smith'); - expect(element('.doc-example-live li:nth-child(1) input').val()) - .toBe('408 555 1212'); - expect(element('.doc-example-live li:nth-child(2) input').val()) - .toBe('john.smith@example.org'); - - element('.doc-example-live li:first a:contains("clear")').click(); - expect(element('.doc-example-live li:first input').val()).toBe(''); - - element('.doc-example-live li:last a:contains("add")').click(); - expect(element('.doc-example-live li:nth-child(3) input').val()) - .toBe('yourname@example.org'); - }); - -
- - */ -var ngControllerDirective = [function() { - return { - scope: true, - controller: '@' - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngCsp - * @priority 1000 - * - * @element html - * @description - * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. - * - * This is necessary when developing things like Google Chrome Extensions. - * - * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). - * For us to be compatible, we just need to implement the "getterFn" in $parse without violating - * any of these restrictions. - * - * AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp` - * it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will - * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will - * be raised. - * - * In order to use this feature put `ngCsp` directive on the root element of the application. - * - * @example - * This example shows how to apply the `ngCsp` directive to the `html` tag. -
-     
-     
-     ...
-     ...
-     
-   
- */ - -var ngCspDirective = ['$sniffer', function($sniffer) { - return { - priority: 1000, - compile: function() { - $sniffer.csp = true; - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngClick - * - * @description - * The ngClick allows you to specify custom behavior when - * element is clicked. - * - * @element ANY - * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon - * click. (Event object is available as `$event`) - * - * @example - - - - count: {{count}} - - - it('should check ng-click', function() { - expect(binding('count')).toBe('0'); - element('.doc-example-live :button').click(); - expect(binding('count')).toBe('1'); - }); - - - */ -/* - * A directive that allows creation of custom onclick handlers that are defined as angular - * expressions and are compiled and executed within the current scope. - * - * Events that are handled via these handler are always configured not to propagate further. - */ -var ngEventDirectives = {}; -forEach( - 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress'.split(' '), - function(name) { - var directiveName = directiveNormalize('ng-' + name); - ngEventDirectives[directiveName] = ['$parse', function($parse) { - return function(scope, element, attr) { - var fn = $parse(attr[directiveName]); - element.bind(lowercase(name), function(event) { - scope.$apply(function() { - fn(scope, {$event:event}); - }); - }); - }; - }]; - } -); - -/** - * @ngdoc directive - * @name ng.directive:ngDblclick - * - * @description - * The `ngDblclick` directive allows you to specify custom behavior on dblclick event. - * - * @element ANY - * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon - * dblclick. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMousedown - * - * @description - * The ngMousedown directive allows you to specify custom behavior on mousedown event. - * - * @element ANY - * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon - * mousedown. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMouseup - * - * @description - * Specify custom behavior on mouseup event. - * - * @element ANY - * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon - * mouseup. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - -/** - * @ngdoc directive - * @name ng.directive:ngMouseover - * - * @description - * Specify custom behavior on mouseover event. - * - * @element ANY - * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon - * mouseover. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMouseenter - * - * @description - * Specify custom behavior on mouseenter event. - * - * @element ANY - * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon - * mouseenter. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMouseleave - * - * @description - * Specify custom behavior on mouseleave event. - * - * @element ANY - * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon - * mouseleave. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMousemove - * - * @description - * Specify custom behavior on mousemove event. - * - * @element ANY - * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon - * mousemove. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngKeydown - * - * @description - * Specify custom behavior on keydown event. - * - * @element ANY - * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon - * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngKeyup - * - * @description - * Specify custom behavior on keyup event. - * - * @element ANY - * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon - * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngKeypress - * - * @description - * Specify custom behavior on keypress event. - * - * @element ANY - * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon - * keypress. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngSubmit - * - * @description - * Enables binding angular expressions to onsubmit events. - * - * Additionally it prevents the default action (which for form means sending the request to the - * server and reloading the current page). - * - * @element form - * @param {expression} ngSubmit {@link guide/expression Expression} to eval. - * - * @example - - - -
- Enter text and hit enter: - - -
list={{list}}
-
-
- - it('should check ng-submit', function() { - expect(binding('list')).toBe('[]'); - element('.doc-example-live #submit').click(); - expect(binding('list')).toBe('["hello"]'); - expect(input('text').val()).toBe(''); - }); - it('should ignore empty strings', function() { - expect(binding('list')).toBe('[]'); - element('.doc-example-live #submit').click(); - element('.doc-example-live #submit').click(); - expect(binding('list')).toBe('["hello"]'); - }); - -
- */ -var ngSubmitDirective = ngDirective(function(scope, element, attrs) { - element.bind('submit', function() { - scope.$apply(attrs.ngSubmit); - }); -}); - -/** - * @ngdoc directive - * @name ng.directive:ngIf - * @restrict A - * - * @description - * The `ngIf` directive removes and recreates a portion of the DOM tree (HTML) - * conditionally based on **"falsy"** and **"truthy"** values, respectively, evaluated within - * an {expression}. In other words, if the expression assigned to **ngIf evaluates to a false - * value** then **the element is removed from the DOM** and **if true** then **a clone of the - * element is reinserted into the DOM**. - * - * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the - * element in the DOM rather than changing its visibility via the `display` css property. A common - * case when this difference is significant is when using css selectors that rely on an element's - * position within the DOM (HTML), such as the `:first-child` or `:last-child` pseudo-classes. - * - * Note that **when an element is removed using ngIf its scope is destroyed** and **a new scope - * is created when the element is restored**. The scope created within `ngIf` inherits from - * its parent scope using - * {@link https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance prototypal inheritance}. - * An important implication of this is if `ngModel` is used within `ngIf` to bind to - * a javascript primitive defined in the parent scope. In this case any modifications made to the - * variable within the child scope will override (hide) the value in the parent scope. - * - * Also, `ngIf` recreates elements using their compiled state. An example scenario of this behavior - * is if an element's class attribute is directly modified after it's compiled, using something like - * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element - * the added class will be lost because the original compiled state is used to regenerate the element. - * - * Additionally, you can provide animations via the ngAnimate attribute to animate the **enter** - * and **leave** effects. - * - * @animations - * enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container - * leave - happens just before the ngIf contents are removed from the DOM - * - * @element ANY - * @scope - * @param {expression} ngIf If the {@link guide/expression expression} is falsy then - * the element is removed from the DOM tree (HTML). - * - * @example - - - Click me:
- Show when checked: - - I'm removed when the checkbox is unchecked. - -
- - .example-leave, .example-enter { - -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - } - - .example-enter { - opacity:0; - } - .example-enter.example-enter-active { - opacity:1; - } - - .example-leave { - opacity:1; - } - .example-leave.example-leave-active { - opacity:0; - } - -
- */ -var ngIfDirective = ['$animator', function($animator) { - return { - transclude: 'element', - priority: 1000, - terminal: true, - restrict: 'A', - compile: function (element, attr, transclude) { - return function ($scope, $element, $attr) { - var animate = $animator($scope, $attr); - var childElement, childScope; - $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { - if (childElement) { - animate.leave(childElement); - childElement = undefined; - } - if (childScope) { - childScope.$destroy(); - childScope = undefined; - } - if (toBoolean(value)) { - childScope = $scope.$new(); - transclude(childScope, function (clone) { - childElement = clone; - animate.enter(clone, $element.parent(), $element); - }); - } - }); - } - } - } -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngInclude - * @restrict ECA - * - * @description - * Fetches, compiles and includes an external HTML fragment. - * - * Keep in mind that Same Origin Policy applies to included resources - * (e.g. ngInclude won't work for cross-domain requests on all browsers and for - * file:// access on some browsers). - * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** - * and **leave** effects. - * - * @animations - * enter - happens just after the ngInclude contents change and a new DOM element is created and injected into the ngInclude container - * leave - happens just after the ngInclude contents change and just before the former contents are removed from the DOM - * - * @scope - * - * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, - * make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`. - * @param {string=} onload Expression to evaluate when a new partial is loaded. - * - * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll - * $anchorScroll} to scroll the viewport after the content is loaded. - * - * - If the attribute is not set, disable scrolling. - * - If the attribute is set without value, enable scrolling. - * - Otherwise enable scrolling only if the expression evaluates to truthy value. - * - * @example - - -
- - url of the template: {{template.url}} -
-
-
-
- - function Ctrl($scope) { - $scope.templates = - [ { name: 'template1.html', url: 'template1.html'} - , { name: 'template2.html', url: 'template2.html'} ]; - $scope.template = $scope.templates[0]; - } - - -
Content of template1.html
-
- -
Content of template2.html
-
- - .example-leave, - .example-enter { - -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - - position:absolute; - top:0; - left:0; - right:0; - bottom:0; - } - - .example-animate-container > * { - display:block; - padding:10px; - } - - .example-enter { - top:-50px; - } - .example-enter.example-enter-active { - top:0; - } - - .example-leave { - top:0; - } - .example-leave.example-leave-active { - top:50px; - } - - - it('should load template1.html', function() { - expect(element('.doc-example-live [ng-include]').text()). - toMatch(/Content of template1.html/); - }); - it('should load template2.html', function() { - select('template').option('1'); - expect(element('.doc-example-live [ng-include]').text()). - toMatch(/Content of template2.html/); - }); - it('should change to blank', function() { - select('template').option(''); - expect(element('.doc-example-live [ng-include]').text()).toEqual(''); - }); - -
- */ - - -/** - * @ngdoc event - * @name ng.directive:ngInclude#$includeContentRequested - * @eventOf ng.directive:ngInclude - * @eventType emit on the scope ngInclude was declared in - * @description - * Emitted every time the ngInclude content is requested. - */ - - -/** - * @ngdoc event - * @name ng.directive:ngInclude#$includeContentLoaded - * @eventOf ng.directive:ngInclude - * @eventType emit on the current ngInclude scope - * @description - * Emitted every time the ngInclude content is reloaded. - */ -var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animator', - function($http, $templateCache, $anchorScroll, $compile, $animator) { - return { - restrict: 'ECA', - terminal: true, - compile: function(element, attr) { - var srcExp = attr.ngInclude || attr.src, - onloadExp = attr.onload || '', - autoScrollExp = attr.autoscroll; - - return function(scope, element, attr) { - var animate = $animator(scope, attr); - var changeCounter = 0, - childScope; - - var clearContent = function() { - if (childScope) { - childScope.$destroy(); - childScope = null; - } - animate.leave(element.contents(), element); - }; - - scope.$watch(srcExp, function ngIncludeWatchAction(src) { - var thisChangeId = ++changeCounter; - - if (src) { - $http.get(src, {cache: $templateCache}).success(function(response) { - if (thisChangeId !== changeCounter) return; - - if (childScope) childScope.$destroy(); - childScope = scope.$new(); - animate.leave(element.contents(), element); - - var contents = jqLite('
').html(response).contents(); - - animate.enter(contents, element); - $compile(contents)(childScope); - - if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { - $anchorScroll(); - } - - childScope.$emit('$includeContentLoaded'); - scope.$eval(onloadExp); - }).error(function() { - if (thisChangeId === changeCounter) clearContent(); - }); - scope.$emit('$includeContentRequested'); - } else { - clearContent(); - } - }); - }; - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngInit - * - * @description - * The `ngInit` directive specifies initialization tasks to be executed - * before the template enters execution mode during bootstrap. - * - * @element ANY - * @param {expression} ngInit {@link guide/expression Expression} to eval. - * - * @example - - -
- {{greeting}} {{person}}! -
-
- - it('should check greeting', function() { - expect(binding('greeting')).toBe('Hello'); - expect(binding('person')).toBe('World'); - }); - -
- */ -var ngInitDirective = ngDirective({ - compile: function() { - return { - pre: function(scope, element, attrs) { - scope.$eval(attrs.ngInit); - } - } - } -}); - -/** - * @ngdoc directive - * @name ng.directive:ngNonBindable - * @priority 1000 - * - * @description - * Sometimes it is necessary to write code which looks like bindings but which should be left alone - * by angular. Use `ngNonBindable` to make angular ignore a chunk of HTML. - * - * @element ANY - * - * @example - * In this example there are two location where a simple binding (`{{}}`) is present, but the one - * wrapped in `ngNonBindable` is left alone. - * - * @example - - -
Normal: {{1 + 2}}
-
Ignored: {{1 + 2}}
-
- - it('should check ng-non-bindable', function() { - expect(using('.doc-example-live').binding('1 + 2')).toBe('3'); - expect(using('.doc-example-live').element('div:last').text()). - toMatch(/1 \+ 2/); - }); - -
- */ -var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); - -/** - * @ngdoc directive - * @name ng.directive:ngPluralize - * @restrict EA - * - * @description - * # Overview - * `ngPluralize` is a directive that displays messages according to en-US localization rules. - * These rules are bundled with angular.js and the rules can be overridden - * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive - * by specifying the mappings between - * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html - * plural categories} and the strings to be displayed. - * - * # Plural categories and explicit number rules - * There are two - * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html - * plural categories} in Angular's default en-US locale: "one" and "other". - * - * While a plural category may match many numbers (for example, in en-US locale, "other" can match - * any number that is not 1), an explicit number rule can only match one number. For example, the - * explicit number rule for "3" matches the number 3. You will see the use of plural categories - * and explicit number rules throughout later parts of this documentation. - * - * # Configuring ngPluralize - * You configure ngPluralize by providing 2 attributes: `count` and `when`. - * You can also provide an optional attribute, `offset`. - * - * The value of the `count` attribute can be either a string or an {@link guide/expression - * Angular expression}; these are evaluated on the current scope for its bound value. - * - * The `when` attribute specifies the mappings between plural categories and the actual - * string to be displayed. The value of the attribute should be a JSON object so that Angular - * can interpret it correctly. - * - * The following example shows how to configure ngPluralize: - * - *
- * 
- * 
- *
- * - * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not - * specify this rule, 0 would be matched to the "other" category and "0 people are viewing" - * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for - * other numbers, for example 12, so that instead of showing "12 people are viewing", you can - * show "a dozen people are viewing". - * - * You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted - * into pluralized strings. In the previous example, Angular will replace `{}` with - * `{{personCount}}`. The closed braces `{}` is a placeholder - * for {{numberExpression}}. - * - * # Configuring ngPluralize with offset - * The `offset` attribute allows further customization of pluralized text, which can result in - * a better user experience. For example, instead of the message "4 people are viewing this document", - * you might display "John, Kate and 2 others are viewing this document". - * The offset attribute allows you to offset a number by any desired value. - * Let's take a look at an example: - * - *
- * 
- * 
- * 
- * - * Notice that we are still using two plural categories(one, other), but we added - * three explicit number rules 0, 1 and 2. - * When one person, perhaps John, views the document, "John is viewing" will be shown. - * When three people view the document, no explicit number rule is found, so - * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category. - * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing" - * is shown. - * - * Note that when you specify offsets, you must provide explicit number rules for - * numbers from 0 up to and including the offset. If you use an offset of 3, for example, - * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for - * plural categories "one" and "other". - * - * @param {string|expression} count The variable to be bounded to. - * @param {string} when The mapping between plural category to its corresponding strings. - * @param {number=} offset Offset to deduct from the total number. - * - * @example - - - -
- Person 1:
- Person 2:
- Number of People:
- - - Without Offset: - -
- - - With Offset(2): - - -
-
- - it('should show correct pluralized string', function() { - expect(element('.doc-example-live ng-pluralize:first').text()). - toBe('1 person is viewing.'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Igor is viewing.'); - - using('.doc-example-live').input('personCount').enter('0'); - expect(element('.doc-example-live ng-pluralize:first').text()). - toBe('Nobody is viewing.'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Nobody is viewing.'); - - using('.doc-example-live').input('personCount').enter('2'); - expect(element('.doc-example-live ng-pluralize:first').text()). - toBe('2 people are viewing.'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Igor and Misko are viewing.'); - - using('.doc-example-live').input('personCount').enter('3'); - expect(element('.doc-example-live ng-pluralize:first').text()). - toBe('3 people are viewing.'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Igor, Misko and one other person are viewing.'); - - using('.doc-example-live').input('personCount').enter('4'); - expect(element('.doc-example-live ng-pluralize:first').text()). - toBe('4 people are viewing.'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Igor, Misko and 2 other people are viewing.'); - }); - - it('should show data-binded names', function() { - using('.doc-example-live').input('personCount').enter('4'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Igor, Misko and 2 other people are viewing.'); - - using('.doc-example-live').input('person1').enter('Di'); - using('.doc-example-live').input('person2').enter('Vojta'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Di, Vojta and 2 other people are viewing.'); - }); - -
- */ -var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { - var BRACE = /{}/g; - return { - restrict: 'EA', - link: function(scope, element, attr) { - var numberExp = attr.count, - whenExp = element.attr(attr.$attr.when), // this is because we have {{}} in attrs - offset = attr.offset || 0, - whens = scope.$eval(whenExp), - whensExpFns = {}, - startSymbol = $interpolate.startSymbol(), - endSymbol = $interpolate.endSymbol(); - - forEach(whens, function(expression, key) { - whensExpFns[key] = - $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' + - offset + endSymbol)); - }); - - scope.$watch(function ngPluralizeWatch() { - var value = parseFloat(scope.$eval(numberExp)); - - if (!isNaN(value)) { - //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, - //check it against pluralization rules in $locale service - if (!(value in whens)) value = $locale.pluralCat(value - offset); - return whensExpFns[value](scope, element, true); - } else { - return ''; - } - }, function ngPluralizeWatchAction(newVal) { - element.text(newVal); - }); - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngRepeat - * - * @description - * The `ngRepeat` directive instantiates a template once per item from a collection. Each template - * instance gets its own scope, where the given loop variable is set to the current collection item, - * and `$index` is set to the item index or key. - * - * Special properties are exposed on the local scope of each template instance, including: - * - * * `$index` – `{number}` – iterator offset of the repeated element (0..length-1) - * * `$first` – `{boolean}` – true if the repeated element is first in the iterator. - * * `$middle` – `{boolean}` – true if the repeated element is between the first and last in the iterator. - * * `$last` – `{boolean}` – true if the repeated element is last in the iterator. - * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**, - * **leave** and **move** effects. - * - * @animations - * enter - when a new item is added to the list or when an item is revealed after a filter - * leave - when an item is removed from the list or when an item is filtered out - * move - when an adjacent item is filtered out causing a reorder or when the item contents are reordered - * - * @element ANY - * @scope - * @priority 1000 - * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These - * formats are currently supported: - * - * * `variable in expression` – where variable is the user defined loop variable and `expression` - * is a scope expression giving the collection to enumerate. - * - * For example: `track in cd.tracks`. - * - * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers, - * and `expression` is the scope expression giving the collection to enumerate. - * - * For example: `(name, age) in {'adam':10, 'amalie':12}`. - * - * * `variable in expression track by tracking_expression` – You can also provide an optional tracking function - * which can be used to associate the objects in the collection with the DOM elements. If no tractking function - * is specified the ng-repeat associates elements by identity in the collection. It is an error to have - * more then one tractking function to resolve to the same key. (This would mean that two distinct objects are - * mapped to the same DOM element, which is not possible.) - * - * For example: `item in items` is equivalent to `item in items track by $id(item)'. This implies that the DOM elements - * will be associated by item identity in the array. - * - * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique - * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements - * with the corresponding item in the array by identity. Moving the same object in array would move the DOM - * element in the same way ian the DOM. - * - * For example: `item in items track by item.id` Is a typical pattern when the items come from the database. In this - * case the object identity does not matter. Two objects are considered equivalent as long as their `id` - * property is same. - * - * @example - * This example initializes the scope to a list of names and - * then uses `ngRepeat` to display every person: - - -
- I have {{friends.length}} friends. They are: - -
    -
  • - [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. -
  • -
-
-
- - .example-repeat-enter, - .example-repeat-leave, - .example-repeat-move { - -webkit-transition:all linear 0.5s; - -moz-transition:all linear 0.5s; - -ms-transition:all linear 0.5s; - -o-transition:all linear 0.5s; - transition:all linear 0.5s; - } - - .example-repeat-enter { - line-height:0; - opacity:0; - } - .example-repeat-enter.example-repeat-enter-active { - line-height:20px; - opacity:1; - } - - .example-repeat-leave { - opacity:1; - line-height:20px; - } - .example-repeat-leave.example-repeat-leave-active { - opacity:0; - line-height:0; - } - - .example-repeat-move { } - .example-repeat-move.example-repeat-move-active { } - - - it('should render initial data set', function() { - var r = using('.doc-example-live').repeater('ul li'); - expect(r.count()).toBe(10); - expect(r.row(0)).toEqual(["1","John","25"]); - expect(r.row(1)).toEqual(["2","Jessie","30"]); - expect(r.row(9)).toEqual(["10","Samantha","60"]); - expect(binding('friends.length')).toBe("10"); - }); - - it('should update repeater when filter predicate changes', function() { - var r = using('.doc-example-live').repeater('ul li'); - expect(r.count()).toBe(10); - - input('q').enter('ma'); - - expect(r.count()).toBe(2); - expect(r.row(0)).toEqual(["1","Mary","28"]); - expect(r.row(1)).toEqual(["2","Samantha","60"]); - }); - -
- */ -var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) { - var NG_REMOVED = '$$NG_REMOVED'; - return { - transclude: 'element', - priority: 1000, - terminal: true, - compile: function(element, attr, linker) { - return function($scope, $element, $attr){ - var animate = $animator($scope, $attr); - var expression = $attr.ngRepeat; - var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/), - trackByExp, trackByExpGetter, trackByIdFn, lhs, rhs, valueIdentifier, keyIdentifier, - hashFnLocals = {$id: hashKey}; - - if (!match) { - throw Error("Expected ngRepeat in form of '_item_ in _collection_[ track by _id_]' but got '" + - expression + "'."); - } - - lhs = match[1]; - rhs = match[2]; - trackByExp = match[4]; - - if (trackByExp) { - trackByExpGetter = $parse(trackByExp); - trackByIdFn = function(key, value, index) { - // assign key, value, and $index to the locals so that they can be used in hash functions - if (keyIdentifier) hashFnLocals[keyIdentifier] = key; - hashFnLocals[valueIdentifier] = value; - hashFnLocals.$index = index; - return trackByExpGetter($scope, hashFnLocals); - }; - } else { - trackByIdFn = function(key, value) { - return hashKey(value); - } - } - - match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); - if (!match) { - throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" + - lhs + "'."); - } - valueIdentifier = match[3] || match[1]; - keyIdentifier = match[2]; - - // Store a list of elements from previous run. This is a hash where key is the item from the - // iterator, and the value is objects with following properties. - // - scope: bound scope - // - element: previous element. - // - index: position - var lastBlockMap = {}; - - //watch props - $scope.$watchCollection(rhs, function ngRepeatAction(collection){ - var index, length, - cursor = $element, // current position of the node - nextCursor, - // Same as lastBlockMap but it has the current state. It will become the - // lastBlockMap on the next iteration. - nextBlockMap = {}, - arrayLength, - childScope, - key, value, // key/value of iteration - trackById, - collectionKeys, - block, // last object information {scope, element, id} - nextBlockOrder = []; - - - if (isArrayLike(collection)) { - collectionKeys = collection; - } else { - // if object, extract keys, sort them and use to determine order of iteration over obj props - collectionKeys = []; - for (key in collection) { - if (collection.hasOwnProperty(key) && key.charAt(0) != '$') { - collectionKeys.push(key); - } - } - collectionKeys.sort(); - } - - arrayLength = collectionKeys.length; - - // locate existing items - length = nextBlockOrder.length = collectionKeys.length; - for(index = 0; index < length; index++) { - key = (collection === collectionKeys) ? index : collectionKeys[index]; - value = collection[key]; - trackById = trackByIdFn(key, value, index); - if(lastBlockMap.hasOwnProperty(trackById)) { - block = lastBlockMap[trackById] - delete lastBlockMap[trackById]; - nextBlockMap[trackById] = block; - nextBlockOrder[index] = block; - } else if (nextBlockMap.hasOwnProperty(trackById)) { - // restore lastBlockMap - forEach(nextBlockOrder, function(block) { - if (block && block.element) lastBlockMap[block.id] = block; - }); - // This is a duplicate and we need to throw an error - throw new Error('Duplicates in a repeater are not allowed. Repeater: ' + expression + - ' key: ' + trackById); - } else { - // new never before seen block - nextBlockOrder[index] = { id: trackById }; - nextBlockMap[trackById] = false; - } - } - - // remove existing items - for (key in lastBlockMap) { - if (lastBlockMap.hasOwnProperty(key)) { - block = lastBlockMap[key]; - animate.leave(block.element); - block.element[0][NG_REMOVED] = true; - block.scope.$destroy(); - } - } - - // we are not using forEach for perf reasons (trying to avoid #call) - for (index = 0, length = collectionKeys.length; index < length; index++) { - key = (collection === collectionKeys) ? index : collectionKeys[index]; - value = collection[key]; - block = nextBlockOrder[index]; - - if (block.element) { - // if we have already seen this object, then we need to reuse the - // associated scope/element - childScope = block.scope; - - nextCursor = cursor[0]; - do { - nextCursor = nextCursor.nextSibling; - } while(nextCursor && nextCursor[NG_REMOVED]); - - if (block.element[0] == nextCursor) { - // do nothing - cursor = block.element; - } else { - // existing item which got moved - animate.move(block.element, null, cursor); - cursor = block.element; - } - } else { - // new item which we don't know about - childScope = $scope.$new(); - } - - childScope[valueIdentifier] = value; - if (keyIdentifier) childScope[keyIdentifier] = key; - childScope.$index = index; - childScope.$first = (index === 0); - childScope.$last = (index === (arrayLength - 1)); - childScope.$middle = !(childScope.$first || childScope.$last); - - if (!block.element) { - linker(childScope, function(clone) { - animate.enter(clone, null, cursor); - cursor = clone; - block.scope = childScope; - block.element = clone; - nextBlockMap[block.id] = block; - }); - } - } - lastBlockMap = nextBlockMap; - }); - }; - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngShow - * - * @description - * The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML) - * conditionally based on **"truthy"** values evaluated within an {expression}. In other - * words, if the expression assigned to **ngShow evaluates to a true value** then **the element is set to visible** - * (via `display:block` in css) and **if false** then **the element is set to hidden** (so display:none). - * With ngHide this is the reverse whereas true values cause the element itself to become - * hidden. - * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **show** - * and **hide** effects. - * - * @animations - * show - happens after the ngShow expression evaluates to a truthy value and the contents are set to visible - * hide - happens before the ngShow expression evaluates to a non truthy value and just before the contents are set to hidden - * - * @element ANY - * @param {expression} ngShow If the {@link guide/expression expression} is truthy - * then the element is shown or hidden respectively. - * - * @example - - - Click me:
-
- Show: - - I show up when your checkbox is checked. - -
-
- Hide: - - I hide when your checkbox is checked. - -
-
- - .example-show, .example-hide { - -webkit-transition:all linear 0.5s; - -moz-transition:all linear 0.5s; - -ms-transition:all linear 0.5s; - -o-transition:all linear 0.5s; - transition:all linear 0.5s; - } - - .example-show { - line-height:0; - opacity:0; - padding:0 10px; - } - .example-show-active.example-show-active { - line-height:20px; - opacity:1; - padding:10px; - border:1px solid black; - background:white; - } - - .example-hide { - line-height:20px; - opacity:1; - padding:10px; - border:1px solid black; - background:white; - } - .example-hide-active.example-hide-active { - line-height:0; - opacity:0; - padding:0 10px; - } - - .check-element { - padding:10px; - border:1px solid black; - background:white; - } - - - it('should check ng-show / ng-hide', function() { - expect(element('.doc-example-live span:first:hidden').count()).toEqual(1); - expect(element('.doc-example-live span:last:visible').count()).toEqual(1); - - input('checked').check(); - - expect(element('.doc-example-live span:first:visible').count()).toEqual(1); - expect(element('.doc-example-live span:last:hidden').count()).toEqual(1); - }); - -
- */ -//TODO(misko): refactor to remove element from the DOM -var ngShowDirective = ['$animator', function($animator) { - return function(scope, element, attr) { - var animate = $animator(scope, attr); - scope.$watch(attr.ngShow, function ngShowWatchAction(value){ - animate[toBoolean(value) ? 'show' : 'hide'](element); - }); - }; -}]; - - -/** - * @ngdoc directive - * @name ng.directive:ngHide - * - * @description - * The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML) - * conditionally based on **"truthy"** values evaluated within an {expression}. In other - * words, if the expression assigned to **ngShow evaluates to a true value** then **the element is set to visible** - * (via `display:block` in css) and **if false** then **the element is set to hidden** (so display:none). - * With ngHide this is the reverse whereas true values cause the element itself to become - * hidden. - * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **show** - * and **hide** effects. - * - * @animations - * show - happens after the ngHide expression evaluates to a non truthy value and the contents are set to visible - * hide - happens after the ngHide expression evaluates to a truthy value and just before the contents are set to hidden - * - * @element ANY - * @param {expression} ngHide If the {@link guide/expression expression} is truthy then - * the element is shown or hidden respectively. - * - * @example - - - Click me:
-
- Show: - - I show up when your checkbox is checked. - -
-
- Hide: - - I hide when your checkbox is checked. - -
-
- - .example-show, .example-hide { - -webkit-transition:all linear 0.5s; - -moz-transition:all linear 0.5s; - -ms-transition:all linear 0.5s; - -o-transition:all linear 0.5s; - transition:all linear 0.5s; - } - - .example-show { - line-height:0; - opacity:0; - padding:0 10px; - } - .example-show.example-show-active { - line-height:20px; - opacity:1; - padding:10px; - border:1px solid black; - background:white; - } - - .example-hide { - line-height:20px; - opacity:1; - padding:10px; - border:1px solid black; - background:white; - } - .example-hide.example-hide-active { - line-height:0; - opacity:0; - padding:0 10px; - } - - .check-element { - padding:10px; - border:1px solid black; - background:white; - } - - - it('should check ng-show / ng-hide', function() { - expect(element('.doc-example-live .check-element:first:hidden').count()).toEqual(1); - expect(element('.doc-example-live .check-element:last:visible').count()).toEqual(1); - - input('checked').check(); - - expect(element('.doc-example-live .check-element:first:visible').count()).toEqual(1); - expect(element('.doc-example-live .check-element:last:hidden').count()).toEqual(1); - }); - -
- */ -//TODO(misko): refactor to remove element from the DOM -var ngHideDirective = ['$animator', function($animator) { - return function(scope, element, attr) { - var animate = $animator(scope, attr); - scope.$watch(attr.ngHide, function ngHideWatchAction(value){ - animate[toBoolean(value) ? 'hide' : 'show'](element); - }); - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngStyle - * - * @description - * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. - * - * @element ANY - * @param {expression} ngStyle {@link guide/expression Expression} which evals to an - * object whose keys are CSS style names and values are corresponding values for those CSS - * keys. - * - * @example - - - - -
- Sample Text -
myStyle={{myStyle}}
-
- - span { - color: black; - } - - - it('should check ng-style', function() { - expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)'); - element('.doc-example-live :button[value=set]').click(); - expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)'); - element('.doc-example-live :button[value=clear]').click(); - expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)'); - }); - -
- */ -var ngStyleDirective = ngDirective(function(scope, element, attr) { - scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { - if (oldStyles && (newStyles !== oldStyles)) { - forEach(oldStyles, function(val, style) { element.css(style, '');}); - } - if (newStyles) element.css(newStyles); - }, true); -}); - -/** - * @ngdoc directive - * @name ng.directive:ngSwitch - * @restrict EA - * - * @description - * The ngSwitch directive is used to conditionally swap DOM structure on your template based on a scope expression. - * Elements within ngSwitch but without ngSwitchWhen or ngSwitchDefault directives will be preserved at the location - * as specified in the template. - * - * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it - * from the template cache), ngSwitch simply choses one of the nested elements and makes it visible based on which element - * matches the value obtained from the evaluated expression. In other words, you define a container element - * (where you place the directive), place an expression on the **on="..." attribute** - * (or the **ng-switch="..." attribute**), define any inner elements inside of the directive and place - * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on - * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default - * attribute is displayed. - * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** - * and **leave** effects. - * - * @animations - * enter - happens after the ngSwtich contents change and the matched child element is placed inside the container - * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM - * - * @usage - * - * ... - * ... - * ... - * - * - * @scope - * @param {*} ngSwitch|on expression to match against ng-switch-when. - * @paramDescription - * On child elements add: - * - * * `ngSwitchWhen`: the case statement to match against. If match then this - * case will be displayed. If the same match appears multiple times, all the - * elements will be displayed. - * * `ngSwitchDefault`: the default case when no other case match. If there - * are multiple default cases, all of them will be displayed when no other - * case match. - * - * - * @example - - -
- - selection={{selection}} -
-
-
Settings Div
-
Home Span
-
default
-
-
-
- - function Ctrl($scope) { - $scope.items = ['settings', 'home', 'other']; - $scope.selection = $scope.items[0]; - } - - - .example-leave, .example-enter { - -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - - position:absolute; - top:0; - left:0; - right:0; - bottom:0; - } - - .example-animate-container > * { - display:block; - padding:10px; - } - - .example-enter { - top:-50px; - } - .example-enter.example-enter-active { - top:0; - } - - .example-leave { - top:0; - } - .example-leave.example-leave-active { - top:50px; - } - - - it('should start in settings', function() { - expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Settings Div/); - }); - it('should change to home', function() { - select('selection').option('home'); - expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Home Span/); - }); - it('should select default', function() { - select('selection').option('other'); - expect(element('.doc-example-live [ng-switch]').text()).toMatch(/default/); - }); - -
- */ -var ngSwitchDirective = ['$animator', function($animator) { - return { - restrict: 'EA', - require: 'ngSwitch', - - // asks for $scope to fool the BC controller module - controller: ['$scope', function ngSwitchController() { - this.cases = {}; - }], - link: function(scope, element, attr, ngSwitchController) { - var animate = $animator(scope, attr); - var watchExpr = attr.ngSwitch || attr.on, - selectedTranscludes, - selectedElements, - selectedScopes = []; - - scope.$watch(watchExpr, function ngSwitchWatchAction(value) { - for (var i= 0, ii=selectedScopes.length; i - - -
-
-
- {{text}} -
-
- - it('should have transcluded', function() { - input('title').enter('TITLE'); - input('text').enter('TEXT'); - expect(binding('title')).toEqual('TITLE'); - expect(binding('text')).toEqual('TEXT'); - }); - - - * - */ -var ngTranscludeDirective = ngDirective({ - controller: ['$transclude', '$element', function($transclude, $element) { - $transclude(function(clone) { - $element.append(clone); - }); - }] -}); - -/** - * @ngdoc directive - * @name ng.directive:ngView - * @restrict ECA - * - * @description - * # Overview - * `ngView` is a directive that complements the {@link ng.$route $route} service by - * including the rendered template of the current route into the main layout (`index.html`) file. - * Every time the current route changes, the included view changes with it according to the - * configuration of the `$route` service. - * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** - * and **leave** effects. - * - * @animations - * enter - happens just after the ngView contents are changed (when the new view DOM element is inserted into the DOM) - * leave - happens just after the current ngView contents change and just before the former contents are removed from the DOM - * - * @scope - * @example - - -
- Choose: - Moby | - Moby: Ch1 | - Gatsby | - Gatsby: Ch4 | - Scarlet Letter
- -
-
- -
$location.path() = {{main.$location.path()}}
-
$route.current.templateUrl = {{main.$route.current.templateUrl}}
-
$route.current.params = {{main.$route.current.params}}
-
$route.current.scope.name = {{main.$route.current.scope.name}}
-
$routeParams = {{main.$routeParams}}
-
-
- - -
- controller: {{book.name}}
- Book Id: {{book.params.bookId}}
-
-
- - -
- controller: {{chapter.name}}
- Book Id: {{chapter.params.bookId}}
- Chapter Id: {{chapter.params.chapterId}} -
-
- - - .example-leave, .example-enter { - -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; - -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; - -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; - -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; - transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; - } - - .example-animate-container { - position:relative; - height:100px; - } - - .example-animate-container > * { - display:block; - width:100%; - border-left:1px solid black; - - position:absolute; - top:0; - left:0; - right:0; - bottom:0; - padding:10px; - } - - .example-enter { - left:100%; - } - .example-enter.example-enter-active { - left:0; - } - - .example-leave { } - .example-leave.example-leave-active { - left:-100%; - } - - - - angular.module('ngView', [], function($routeProvider, $locationProvider) { - $routeProvider.when('/Book/:bookId', { - templateUrl: 'book.html', - controller: BookCntl, - controllerAs: 'book' - }); - $routeProvider.when('/Book/:bookId/ch/:chapterId', { - templateUrl: 'chapter.html', - controller: ChapterCntl, - controllerAs: 'chapter' - }); - - // configure html5 to get links working on jsfiddle - $locationProvider.html5Mode(true); - }); - - function MainCntl($route, $routeParams, $location) { - this.$route = $route; - this.$location = $location; - this.$routeParams = $routeParams; - } - - function BookCntl($routeParams) { - this.name = "BookCntl"; - this.params = $routeParams; - } - - function ChapterCntl($routeParams) { - this.name = "ChapterCntl"; - this.params = $routeParams; - } - - - - it('should load and compile correct template', function() { - element('a:contains("Moby: Ch1")').click(); - var content = element('.doc-example-live [ng-view]').text(); - expect(content).toMatch(/controller\: ChapterCntl/); - expect(content).toMatch(/Book Id\: Moby/); - expect(content).toMatch(/Chapter Id\: 1/); - - element('a:contains("Scarlet")').click(); - content = element('.doc-example-live [ng-view]').text(); - expect(content).toMatch(/controller\: BookCntl/); - expect(content).toMatch(/Book Id\: Scarlet/); - }); - -
- */ - - -/** - * @ngdoc event - * @name ng.directive:ngView#$viewContentLoaded - * @eventOf ng.directive:ngView - * @eventType emit on the current ngView scope - * @description - * Emitted every time the ngView content is reloaded. - */ -var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile', - '$controller', '$animator', - function($http, $templateCache, $route, $anchorScroll, $compile, - $controller, $animator) { - return { - restrict: 'ECA', - terminal: true, - link: function(scope, element, attr) { - var lastScope, - onloadExp = attr.onload || '', - animate = $animator(scope, attr); - - scope.$on('$routeChangeSuccess', update); - update(); - - - function destroyLastScope() { - if (lastScope) { - lastScope.$destroy(); - lastScope = null; - } - } - - function clearContent() { - animate.leave(element.contents(), element); - destroyLastScope(); - } - - function update() { - var locals = $route.current && $route.current.locals, - template = locals && locals.$template; - - if (template) { - clearContent(); - var enterElements = jqLite('
').html(template).contents(); - animate.enter(enterElements, element); - - var link = $compile(enterElements), - current = $route.current, - controller; - - lastScope = current.scope = scope.$new(); - if (current.controller) { - locals.$scope = lastScope; - controller = $controller(current.controller, locals); - if (current.controllerAs) { - lastScope[current.controllerAs] = controller; - } - element.children().data('$ngControllerController', controller); - } - - link(lastScope); - lastScope.$emit('$viewContentLoaded'); - lastScope.$eval(onloadExp); - - // $anchorScroll might listen on event... - $anchorScroll(); - } else { - clearContent(); - } - } - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:script - * - * @description - * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the - * template can be used by `ngInclude`, `ngView` or directive templates. - * - * @restrict E - * @param {'text/ng-template'} type must be set to `'text/ng-template'` - * - * @example - - - - - Load inlined template -
-
- - it('should load template defined inside script tag', function() { - element('#tpl-link').click(); - expect(element('#tpl-content').text()).toMatch(/Content of the template/); - }); - -
- */ -var scriptDirective = ['$templateCache', function($templateCache) { - return { - restrict: 'E', - terminal: true, - compile: function(element, attr) { - if (attr.type == 'text/ng-template') { - var templateUrl = attr.id, - // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent - text = element[0].text; - - $templateCache.put(templateUrl, text); - } - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:select - * @restrict E - * - * @description - * HTML `SELECT` element with angular data-binding. - * - * # `ngOptions` - * - * Optionally `ngOptions` attribute can be used to dynamically generate a list of `
- - - it('should check ng-options', function() { - expect(binding('{selected_color:color}')).toMatch('red'); - select('color').option('0'); - expect(binding('{selected_color:color}')).toMatch('black'); - using('.nullable').select('color').option(''); - expect(binding('{selected_color:color}')).toMatch('null'); - }); - - - */ - -var ngOptionsDirective = valueFn({ terminal: true }); -var selectDirective = ['$compile', '$parse', function($compile, $parse) { - //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000007777000000000000000000088888 - var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/, - nullModelCtrl = {$setViewValue: noop}; - - return { - restrict: 'E', - require: ['select', '?ngModel'], - controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) { - var self = this, - optionsMap = {}, - ngModelCtrl = nullModelCtrl, - nullOption, - unknownOption; - - - self.databound = $attrs.ngModel; - - - self.init = function(ngModelCtrl_, nullOption_, unknownOption_) { - ngModelCtrl = ngModelCtrl_; - nullOption = nullOption_; - unknownOption = unknownOption_; - } - - - self.addOption = function(value) { - optionsMap[value] = true; - - if (ngModelCtrl.$viewValue == value) { - $element.val(value); - if (unknownOption.parent()) unknownOption.remove(); - } - }; - - - self.removeOption = function(value) { - if (this.hasOption(value)) { - delete optionsMap[value]; - if (ngModelCtrl.$viewValue == value) { - this.renderUnknownOption(value); - } - } - }; - - - self.renderUnknownOption = function(val) { - var unknownVal = '? ' + hashKey(val) + ' ?'; - unknownOption.val(unknownVal); - $element.prepend(unknownOption); - $element.val(unknownVal); - unknownOption.prop('selected', true); // needed for IE - } - - - self.hasOption = function(value) { - return optionsMap.hasOwnProperty(value); - } - - $scope.$on('$destroy', function() { - // disable unknown option so that we don't do work when the whole select is being destroyed - self.renderUnknownOption = noop; - }); - }], - - link: function(scope, element, attr, ctrls) { - // if ngModel is not defined, we don't need to do anything - if (!ctrls[1]) return; - - var selectCtrl = ctrls[0], - ngModelCtrl = ctrls[1], - multiple = attr.multiple, - optionsExp = attr.ngOptions, - nullOption = false, // if false, user will not be able to select it (used by ngOptions) - emptyOption, - // we can't just jqLite('